配置
ORM利用第三方工具连接数据库,可通过setting进行配置
SQLite
Django默认使用SQLite数据库
# settings.py# ---------------------------------DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3','NAME': os.path.join(BASE_DIR, 'db.sqlite3'),}}
Mysql
# 使用MySQL 修改settings.py中配置DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'DBName', # 数据库名称'USER': 'root', # 数据库用户名'PASSWORD': '11111', # 数据库密码'HOST': 'localhost', # 数据库地址'PORT': '3306' # 数据库端口'OPTIONS': {'init_command': 'SET storage_engine=INNODB;'} # 设置刷卡机的引擎INNODB# 出现 Unknown system variable 'storage_engine'错误时,改为以下'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB;'}}}# 默认使用MySQLDB连接mysql,python3中没有MySQLDB,需要修改Django默认连接MySQL方式在项目同名目录下的 __init__.py中添加-------------------------------import pymysql # 需要先安装pymysql pip3 install pymysqlpymysql.install_as_MySQLdb()# 也可直接安装 pip3 install mysqlclient Django 2.2 以后必须使用mysqlclient
mysql 8.0+ 错误
# 使用mysql 8.0+版本的包django.db.utils.OperationalError信息# 因为MySQL8.0版本的密码加密方式发生了改变,cha2加密方法, 需改回原加密方式ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'newpassword';FLUSH PRIVILEGES;
ORM表操作
# 使用MySQL 修改settings.py中配置DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'DBName', # 数据库名称'USER': 'root', # 数据库用户名'PASSWORD': '11111', # 数据库密码'HOST': 'localhost', # 数据库地址'PORT': '3306' # 数据库端口}}# 默认使用MySQLDB连接mysql,python3中没有MySQLDB,需要修改Django默认连接MySQL方式在项目同名目录下的 __init__.py中添加-------------------------------import pymysql # 需要先安装pymysql pip3 install pymysqlpymysql.install_as_MySQLdb()
字段总结
# int自增列,必须参数 primary_key=Truemodels.AutoField()# bigint自增列,必须参数 primary_key=Truemodels.BigAutoField()# 字符字段 max_length 必须设置models.CharField()# --------以下数据库中存储为 字符串-------------ModelForm 和 Django Admin 会对字段验证models.EmailField() #models.IPAddressField()models.URLField()models.SlugField()models.UUIDField() # 不重复的一串随机字符models.FielPathField()models.FielField()models.ImageField()models.CommaSeparatedIntegerField()#-------------------------------------------# 时间类 插入数据时可以是datetime类型 也可以是字符串 ‘2017-11-11’models.DateTimeField()models.DateField()# 整型字段models.IntegerField()# 浮点字段models.FloatField()# decimal字段models.DecimalField(max_digits=30, decimal_places=10)# 枚举(Django) 选项固定不变时choices = ((1, '项1'), (2, '项2'))models.IntegerField(choices=choices) # 也可以是CharField# 外键models.ForeignKey('类名')参数(对数据库列):max_length 长度default 默认值null=True 可以为空primary_key=True 主键db_index=True 索引unique=True 唯一索引unique_for_date 为时间创建索引unique_for_monthunique_for_year联合索引class Meta:unique_together = () # 联合唯一索引index_together = () # 联合索引参数(对Django Admin):blank=True 可以为空verbose_name 显示名称editable=False 隐藏help_text 提示信息choices 下拉框error_messages 错误信息validators 自定义正则表达式-------------------error_messages={'c1': '优先错误1', 'c2': '优先错误2', 'c3': '优先错误3'}validators = [RegexValidator(regex='root_\d+', message='错了', code='c1'),RegexValidator(regex='root_a\d+', message='又错了', code='c2'),RegexValidator(message='又又错了', code='c3'),]-------------------
在模板中实现显示choices的文本
# 使用 get_xxx_display 方法 xxx为字段名{{ obj.get_sex_display }} {{ obj.sex }}
ORM数据操作
单表 增删改查
# 新增obj = models.UserGroup.objects.create(title='销售部') # obj为新增对象models.UserInfo.objects.create(user='root', password='12345', age=18, ug_id=1)# 查找group_list = models.UserGroup.objects.all() # 查找所有数据# group_list QuerySet类型 (列表) 里面包含 UserGroup对象# values()group_list = models.UserGroup.objects.all().values(可指定字段) # 查找所有数据# group_list QuerySet类型 (列表) 里面包含 字典# values_list()group_list = models.UserGroup.objects.all().values_list(可指定字段) # 查找所有数据# group_list QuerySet类型 (列表) 里面包含 元组# 条件查询models.UserGroup.objects.filter(id=1) # id__gt=1 id>1 id__lt=1 id<1# 删除models.UserGroup.objects.filter(id=2).delete()# 更新models.UserGroup.objects.filter(id=2).update(title='IT部')
进阶操作
# 排序 order_by('-id', 'name') => order by id desc, namemodels.UserInfo.objects.all().order_by('-id', 'name') # - 降序# 分组 聚合函数 annotatefrom django.db.models import Count,Sum,Max,Minres = models.UserInfo.objects.values('ut_id').annotate(c=Count('id'))res.query # 查看生成的SQL语句# SELECT ut_id, COUNT(id) AS c FROM app01_userinfo GROUP BY ut_id# having filter在annotate后面为having,在前面则为wheremodels.UserInfo.objects.values('ut_id').annotate(c=Count('id')).filter(c_gt=2)# filter 条件 where, havingmodels.UserInfo.objects.filter(id=1, age=2) # id=1 and age=2# 多个条件之间是 and 关系, or 使用 Q"""id__gt = 1 => id>1 # 大于id__lt = 5 => id<5 # 小于id__lte = 2 => id<=2 # 小于等于id__gte = 1 => id>=1 # 大于等于id__in = [1,2,3] => id in (1,2,3) # inid__range = [1,4] => id between 1 and 4name__startswith='xxx' # 以xxx开头name__contains='xxx' # 包含xxx"""# 不等于 排除models.UserInfo.objects.exclude(id=1) # id != 1
高级操作
from django.db.models import F, Q# F 获取原来值models.UserInfo.objects.all().update(age=F('age')+1) # 给所有age+1# Q 构造复杂查询条件 组合搜索 组合条件# 直接写 or条件models.UserInfo.objects.filte(Q(id=1) | Q(id=2) ) # id=1 or id=2# 嵌套组合(推荐)q1 = Q()q1.connector = 'OR'q1.children.append(('id', 1))q1.children.append(('id', 10))q1.children.append(('id', 9))q2 = Q()q2.connector = 'OR'q1.children.append(('age__gt', 10))q1.children.append(('age__lt', 6))con = Q()con.add(q1, 'AND')con.add(q2, 'AND')models.UserInfo.objects.filte( con )# (id=1 or id=10 or id=9) and (age>10 or age<6)# extra 额外查询条件以及相关表,排序# select select_params 映射"""select id, (select count(1) from app01_usergroup where id=9 or id<5) as n from app01_userinfo"""models.UserInfo.objects.all().extra(select={'n': 'select count(1) from app01_usergroup where id=%s or id<%s'},select_params=[8,5])# where params 条件 就是where filter()功能models.UserInfo.objects.extra(where=['id=%s or id=2', 'name=%s'],params=[1, 'lishi'])# order_by 排序models.UserInfo.objects.extra(order_by=['-id', 'age'])# table 笛卡尔积models.UserInfo.objects.extra(table=['app01_usergroup'])"""以上可以组合使用"""
执行原生SQL语句
from django.db import connection, connectionscursor = connection.cursor() # 默认连接 default数据库cursor = connections['default'].cursor() # 连接指定数据库# 执行SQL语句cursor.execute('sql语句', 参数)# 获取结果cursor.fetchone()cursor.fetchall()"""使用models对象row()执行sql语句,返回结果为该对象"""
其他
""" distinct 去重 """# mysql sqlite 中distinct()不能传参数models.UserInfo.objects.values('nid').distinct()# PostgreSQLmodels.UserInfo.objects.distinct('nid')# sql: select distinct nid from userinfo""" reverse 反转 前面必须是order_by"""models.UserInfo.objects.all().order_by('id').reverse() # reverse后相对于 '-id'""" only 指定查询的字段"""models.UserInfo.objects.all().only('id', 'name') # 对象中只可id,name# 取其他的字段会从新发送sql请求""" defer 除指定字段外其他字段都获取"""models.UserInfo.objects.all().defer('name') # 只有name字段没有,如果获取name字段则会从新发送sql请求""" using 指定数据库 默认是default数据库"""models.UserInfo.objects.all().using('db2')""" dates"""models.UserInfo.objects.dates('ctime', 'day', 'DESC')"""year, month, dayyear: 年-月-01 只有年份 月日填充01month: 年-月-01 只有年月 日填充01day: 年-月-日 显示 年-月-日DESC, ASC"""""" datetimes""""""year,month,day,hour,minute,secondtzinfo 设定时区pip3 install pytz # 需要安装import pytzpytz.all_timezones # 所有时区pytz.timezone('Asiz/Shanghai') # +8"""models.UserInfo.objects.datetime('ctime', 'hour', tzinfo=pytz.UTC)models.UserInfo.objects.datetime('ctime', 'hour', tzinfo=pytz.timezone('Asiz/Shanghai'))"""none 什么都不取"""""" aggregate 聚合函数 不分组 将整个表作为一组"""models.UserInfo.objects.aggregate(ug_c=Count('ut_id', distinct=True), u_c=Count('id')) # 对ut_id 进行去重统计 id不去重统计""" count 计算个数"""""" get 找到一个正常 找到多个或没有异常"""""" bulk_create 批量插入"""objs = [models.UserInfo(name='a1'),models.UserInfo(name='a2')]models.UserInfo.objects.bulk_create(objs, 10)# 10表示一次插入10数据,有100条数据就要分10次插入""" get_or_create 存在则获取,不存在则参加"""# obj 数据对象 created boolobj,created = models.UserInfo.objects.get_or_create(username='root',defaults={'u_id':2,...}) # 根据username查询, 有返回数据, 没有添加"""update_or_create 有则更新 没有则创建"""obj,created = models.UserInfo.objects.update_or_create(username='root',defaults={'u_id':2,...})""" first 取第一个last 取最后一个in_bulk 根据主键进行查询 同in"""
联表操作
Django2.x 关联表必填的 on_delete
| on_delete值 | 删除关联表时操作 |
|---|---|
None |
|
models.CASCADE |
与之关联也删除 |
models.DO_NOTHING |
什么也不做 |
models.PROTECT |
引发错误ProtectedError |
SET_NULL |
将值设置为null,前提FK字段需要设置null=True |
models.SET_DEFAULT |
将值设置为FK中default=默认值的值 |
| `` | |
| `` |
on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为on_delete=models.CASCADE, # 删除关联数据,与之关联也删除on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError# models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True)on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)# models.ForeignKey('关联表', on_delete=models.SET_DEFAULT, default='默认值')on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)on_delete=models.SET, # 删除关联数据,a. 与之关联的值设置为指定值,设置:models.SET(值)b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
正向操作
user = models.UserInfo.objects.all().first() # 获取一条数据user.ut.title # 自动进行跨表获取 ut是FK字段 会发送SQL查询请求# 使用values时结果不能跨表 只能在查询是跨表 FK字段名__跨表字段名users = models.UserInfo.objects.all().values('id', 'name', 'ut__title')# 查询时主动连表 (数据量少)users = models.UserInfo.objects.all().select_related('ut')# 不做连表查询,分两次查询 (数据量大)models.UserInfo.objects.all().prefetch_related('ut')
反向操作
使用:小写表名_set
group = models.UserGroup.objects.all().first()# 获取当前组下所有usergroup.userinfo_set.all() # 自动反向关联# 一对一时OneToOne 无需加_set 也没有all()方法group.userinfo.name# ForeignKey() 中 添加参数 related_query_name = 'xx'# 则反向查询时 xx_set# ForeignKey() 中 添加参数 related_name = 'xx'# 则反向查询时 直接 .xx 获取
Django 2.x 中的ForeignKey
# Django 2.0 中 foreignkey必须添加on_delete参数# 不加则报 以下错误# TypeError: __init__() missing 1 required positional argument: 'on_delete'user = models.ForeignKey(User, on_delete=models.CASADE)
OneToOne
ForeignKey + unique
user = models.OneToOneField(to='UserInfo', to_field='id')
多对多
自定义第三张表
class Boy(models.Model):name = models.CharField(max_length=32)class Girl(models.Model):nick = models.CharField(max_length=32)class Love(models.Model):b = models.ForeignKey('Boy')g = models.ForeignKey('Girl')# 联合唯一索引class Meta:unique_together = [('g', 'b'),]-----------------------------------------# 得到指定男孩有关系的女孩boy = models.Boy.objects.filter(name='张三').first()love_list = boy.love_set.all() # 反向拿到 lovefor row in love_list:row.g.nick # 得到女孩# 跨表love_list = models.Love.objects.filter(b__name='张三')for i in love_list:i.b.nikc # 没循环一次,跨表查询一次# 查询时跨表 (推荐)# 结果是字典girls = models.Love.objects.filter(b__name='张三').values('g__nick')# 结果是对象girls = models.Love.objects.filter(b__name='张三').select_related('g')
Django自动生成第三张表 ManyToManyField
"""自动生成的第三张表只能有3列 id boy_id girl_id如果对第三张表没有其他字段要求,可使用自动生成"""class Boy(models.Model):name = models.CharField(max_length=32)# 放在boy 和 girl中都相同, 不能直接操作第三张表m = models.ManyToManyField('Girl') # 会自动生成 app01_boy_m 表class Girl(models.Model):nick = models.CharField(max_length=32)----------------------------------------boy = models.Boy.objects.filter(name='张三').first()boy.m.add(2) # 增加 boy和id为2的girl关系 参数可写多个boy.m.remove(1) # 删除boy.m.set([1,]) # 重置,先清空再添加boy.m.all() # 结果 QuerySet 中为girl对象boy.m.filter(nick='小计') # 赛选, 只能是girl中的字段boy.m.clear() # 清空和boy相关的记录# 反向girl = models.Girl.objects.filter(nick='小计').first()girl.boy_set.all() # 得到和girl相关的
ManyToManyField + 自定义第三张表
class Boy(models.Model):name = models.CharField(max_length=32)m = models.ManyToManyField('Girl',through='Love',through_fields=('b','g')) # 指定使用Love为第三表,不自动创建,class Girl(models.Model):nick = models.CharField(max_length=32)class Love(models.Model):b = models.ForeignKey('Boy')g = models.ForeignKey('Girl')"""不能使用 add remove set """
函数
内置函数可使用extra实现
from django.db.models import functions
内置时间函数
# 只获取时间的yeramodels.Article.objects.annotate(ct=functions.Extract('create_time', 'YEAR_MONTH')) # 201809models.Article.objects.annotate(ct=functions.ExtractYear('create_time'))models.Article.objects.annotate(ct=functions.Trunc('create_time', 'YEAR')) ## 内置函数不灵活, 使用extra()models.Article.objects.extra(select={'ct': 'date_format(create_time, "%%Y-%%m")'}).values('ct')
内置函数
# Cast 类型转换models.Article.objects.annotate(c=functions.Cast('id', FloatField()))# Coalescemodels.Article.objects.annotate(c=functions.Coalesce('title', 'name')) # 如果title不为空则c = title 如果title为空则c=name# Concat 拼接字段models.Article.objects.annotate(c=functions.Concat('title', 'name', '更多的字段...')) # 拼接指定字段models.Article.objects.annotate(c=functions.Concat('title', Value('-') 'name')) # 拼接指定字段, 常量用value# Greatest() 获取较大值 Least() 获取较小值# Length() 长度# Lower() Upper() 大小写转换# Now() 当前时间# substr('字段名', 起始位置, 长度) 子序列
自定义函数
from django.db.models.functions.base import Funcclass YearMonthFunc(Func):function = 'DATE_FORMAT'template = '%(function)s(%(expressions)s, "%%Y-%%m")'def __init__(self, expression, **extra):expressions = [expression]super(YearMonthFunc, self).__init__(*expression, **extra)
事务
from django.db import transactiontry:# 开启事务with transaction.atomic():# 数据库操作1# 数据库操作2except Exception as e:pass
Django 使用 mysqlclient
django2.2后不能使用pymysql包,推荐使用mysqlclient,使用pymysql包会报错版本过低
依赖安装
- Ubuntu:
sudo apt-get install python3-dev default-libmysqlclient-dev - CentOS:
sudo yum install python3-devel mysql-devel - macOS:
brew install mysql-connector-c
Mac OS 需修改
修改 /usr/local/bin/mysql_config 文件将# on macOS, on or about line 112:# Create optionslibs="-L$pkglibdir"libs="$libs -l "该为# Create optionslibs="-L$pkglibdir"libs="$libs -lmysqlclient -lssl -lcrypto"
安装
pip install mysqlclient
Mac OS 安装问题
library not found for -lssl
ld: library not found for -lsslclang: error: linker command failed with exit code 1 (use -v to see invocation)error: command 'gcc' failed with exit status 1
解决办法:env LDFLAGS="-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib" pip install mysqlclient
zippered unknown enumerated scalar
ld: malformed file/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libSystem.tbd:4:18: error: unknown enumerated scalarplatform: zippered^~~~~~~~file '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libSystem.tbd'clang: error: linker command failed with exit code 1 (use -v to see invocation)error: command 'gcc' failed with exit status 1
解决办法:将xcode更新到最新版本
