Q查询进阶操作 ORM查询优化 only与defer select_related与prefetch_related ORM事务 ORM常用字段类型 ORM常用字段参数
Q查询进阶操作
from django.db.models import Q
q_obj = Q() # 1.产生q对象
q_obj.connector = 'or' # 默认多个条件的连接是and可以修改为or
q_obj.children.append(('pk', 1)) # 2.添加查询条件
q_obj.children.append(('price__gt', 2000)) # 支持添加多个
res = models.Book.objects.filter(q_obj) # 查询支持直接填写q对象
print(res)
如下图查询条件的左边到底是什么?
pk 和 'pk'是有很大区别的!
pk是一个变量,或者说是一个关键字实参。
'pk'是一个字符串。
使用Q查询可以通过输入字符串来控制如何查询。
首先Q查询,其实使用的是Q类产生的对象:
产生Q对象:
Q对象里面可以存放查询条件。
children.append
使用Q对象.children.append
方法可以往Q对象里面添加多个查询条件。
查询条件添加好之后,直接将Q对象放入filter:
使用append添加查询条件,条件之间默认是and关系。
也可以修改成or关系:
使用q_obj.connector = 'or'
图书查询功能
把查询条件变成字符串,意味着可以与用户互动。
用户的输入就是字符串,获取到用户输入的字符串,就可以从数据库拿到数据。
需求:利用q查询进阶操作编写一个简易版本的图书查询功能。
ORM查询优化
惰性查询
看如下例子:
为什么日志不打印??
orm的查询默认都是惰性查询。什么叫惰性查询?
因为查询的结果res没有用到,所以orm干脆不执行,可以减轻一点数据库的负担。
自动分页 limit
举例:我们的表里面有几亿条数据!然后使用orm进行查询。
orm的查询自带分页处理:
orm底层写的sql使用了LIMIT关键字,这里就是直接限制展示的条数为21.
only与defer
'''数据对象+含有指定字段对应的数据'''
# res = models.Book.objects.only('title', 'price')
# print(res) # queryset [数据对象、数据对象]
# for obj in res:
# print(obj.title) # 点击括号内填写的字段 不走SQL查询
# print(obj.price)
# print(obj.publish_time) # 可以点击括号内没有的字段获取数据 但是会走SQL查询
res = models.Book.objects.defer('title', 'price')
# print(res) # queryset [数据对象、数据对象]
for obj in res:
# print(obj.title) # 点击括号内填写的字段 走SQL查询
# print(obj.price)
print(obj.publish_time) # 点击括号内没有的字段获取数据 不走SQL查询
请看如下例子:
all方法可以拿到所有的书籍对象,数据全在对象里。通过values方法可以拿到书籍的name字段、price字段。name、price存在一个queryset结果集里,格式是列表套字典。并且这个字典是无法使用点的方式取值的。
only
得到的结果变成列表套数据对象(此时是书籍对象)。
此时我们就可以通过点的方式取值:
同时这个only产生的书籍对象,还可以点only括号内没有的字段:
点这个不存在的字段时,only产生的对象肯定也没有相应的数据。此时该对象就会使用sql对数据库进行查询获得数据。并且每次点这个不存在的字段时都会查一次数据库。
defer
defer和only相反,defer括号里没有的字段会在第一次SQL查询后封装到对象中。
如这里obj.publish_time
是一个括号中没出现的字段,这种字段的数据会封装到BOOK对象中。反而是出现在括号内的字段,需要额外的sql来进行数据的查询。
总结:对象中有的数据,就不会再执行SQL进行查询,因为这会增加数据库的负担。当对象想使用自己内部没有的数据时,就会调用SQL语句进行查询。
select_related与prefetch_related
select_related与prefetch_related里面都只能填写一对多、一对一外键字段,不支持多对多外键。
# res = models.Book.objects.all()
# for obj in res:
# print(obj.publish.name) # 每次查询都需要走SQL
# res = models.Book.objects.select_related('authors') # 先连表后查询封装
# res1 = models.Author.objects.select_related('author_detail') # 括号内不支持多对多字段 其他两个都可以
# print(res1)
# for obj in res:
# print(obj.publish.name) # 不再走SQL查询
res = models.Book.objects.prefetch_related('publish') # 子查询
for obj in res:
print(obj.publish.name)
select_related
示例:
数据对象点出版社的字段name 出版社的名字肯定不会封装在book对象 所以就需要临时写SQL去数据库查询
但是这样会写很多SQL,增加数据库负担。
不想走SQL,意味着要在第一句SQL里全部查询到。
这时候就要使用select_related
:
底层是帮你把书籍表和出版社表进行连表操作。
这样就后续调用就不需要走sql了:因为数据都封装到对象了。
prefetch_related
prefetch_related的底层是子查询。
先查一张表再查另外一张表,最后把数据封装到对象。
ORM事务操作
事务复习
"""
1.事务的四大特性(ACID)
原子性、一致性、隔离性、持久性
2.相关SQL关键字
start transaction;
rollback;
commit;
savepoint;
3.相关重要概念
脏读、幻读、不可重复读、MVCC多版本控制...
"""
django orm提供了至少三种开启事务的方式
方式1: 全局有效 配置文件数据库相关添加键值对
"ATOMIC_REQUESTS": True每次请求所涉及到的orm操作同属于一个事务
默认每一次网络请求,涉及到的所有orm操作同属于一个事务。如果哪个orm报错了,事务会自动回滚。
比如:
对于这次请求,只要程序执行到了return,事务就算已经执行成功了。也就是说return后面报错了,数据库也还是会新增数据。
如果不配置django事务,默认情况下是没有事务的,代码走到哪里就是哪。可能出现前几个orm成功,后几个orm失败的情况。
方式2: 局部有效 装饰器
from django.db import transaction
@transaction.atomic
def index():pass
这个是局部开启:
用装饰器装饰视图函数,这个视图函数内部就属于一个事务。只要函数内报错,事务就会回滚。数据库会恢复到进行事务之前的状态。
极限情况:(return后面必须返回httprespon对象)
方式3: 局部有效 with上下文管理
from django.db import transaction
def reg():
with transaction.atomic():
pass
with的子代码内是一个事务:(这种方式用于更小的局部,只能在某个代码块)
示例:
ORM常用字段类型
AutoField
primary_key=True
CharField
max_length
IntegerField
BigIntergerField
DecimalField
max_digits decimal_places
DateField
auto_now auto_now_add
DateTimeField
auto_now auto_now_add
BooleanField
传布尔值自动存0或1
TextField
存储大段文本
有个数限制的使用CharField(用户名)。
个数不固定的使用TextField(小说)。
EmailField
存储邮箱格式数据
FileField
传文件对象 自动保存到提前配置好的路径下并存储该路径信息
主库保存的是文件路径!其他数据库里保存具体的文件数据。
自定义字段
比如django orm中没有专门的char字段。Charfield实际是数据库中的varchar。
orm给我们提供的字段并不是没有使用到char字段的:
'UUIDField':'char(32)'
'IPAddressField':'char(15)'
但是你用这些字段去完成别的事,不符合变量名的见名知义。
所以,我们可以通过自定义字段类型的方式,定义出char字段:
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
return 'char(%s)' % self.max_length
如下图,orm这些字段都继承于父类Field:
重写字段类:
使用自定义字段:
更多orm字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
ORM字段与MySQL字段对应关系
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
ORM常用字段参数
primary_key 主键
verbose_name 注释
max_length 字段长度
max_digits 小数总共多少位
decimal_places 小数点后面的位数
auto_now 每次操作数据自动更新事件
auto_now_add 首次创建自动更新事件后续不自动更新
null 允许字段为空
default 字段默认值
unique 唯一值
db_index 给字段添加索引
choices 当某个字段的可能性能够被列举完全的情况下使用
性别、学历、工作状态、...
class User(models.Model):
name = models.CharField(max_length=32)
info = MyCharField(max_length=64)
# 提前列举好对应关系
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice,null=True)
user_obj = User.objects.filter(pk=1).first()
user_obj.gender
user_obj.get_gender_display()
to 关联表
to_field 关联字段(不写默认关联数据主键)
on_delete
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
choices参数(重点)
当某个字段的可能性能够被列举完全的情况下使用
比如:性别 学历 工作状态
先在类中写好对应关系:
然后gender这个字段就可以用整数类型存:
默认值的设置:
display
可以通过对应关系获取整数对应的字符串:
如这里pk=1的user_obj里gender字段为1,数字一对应的是男性,就可以通过display获取。
如果没有对应关系:
display方法就会返回数据库中存储的值
示例1:
使用这种方式,可以节省数据库空间,用更小的字段存储数据。
示例2:工作经验
示例3:生源由来
总结:相当于在对象中存储了更多的数据。