Query 是如何工作的
Django QuerySet 是懒执行的, 只有访问到对应数据的时候, 才会去访问数据库. 另外如果你再次读取查询到的数据, 将不会触发数据库的访问, 而是直接从缓存获取.
比如
- # 这里不会访问数据库, origins 只是一个查询 query, 不是数据实例
- origins = queryset.filter(status__in=[0, 2])
- # 这里会访问数据库, 将 origins 中的查询 query 与此 update 语句拼在一起组成一个 sql 语句
- origins.update(status=1)
- # 这里的 origins, 是再次执行查询之后的结果, 因此, 结果为空集
- # 如果此时认为 origins 是之前查询的结果集, 就会出错
- for origin in origins:
- self.after_confirm(origin, project_id)
多使用 query 的 count()函数代替 for 循环计数
对 1530 条数据做 for 循环计数的速度是 0.2~0.3s
而用 count 只需要 0.007s 左右
Django 目前不提供外键或多对多的关系跨越多个数据库的支持. 如果你使用路由器分割模型对不同的数据库, 任何 FOREIGNKEY 和多对多关系的模型定义必须是单个数据库的内部.
复制模型数据
获取 model_object 值的方式
model.var
model 中定义了为 IntegerField 的属性取出来是 int
将 model_object 转成字典
model.__dict__ 或者 model_to_dict(model)
复制模型数据
- # 在主数据库创建一个订单副本
- # id 也会相应复制, 但 created_time 和 modified_time 不会
- order = Order.objects.using('qtr').last()
- OrderCopy.objects.create(**model_to_dict(order), project_id='qtr')
外键的反向引用
Tag.objects.filter(project_tag__project_id=project_id)
ProjectTag 表的 tag 字段外键到了 Tag 表的 id 字段, 并且定义了 related_name='project_tag'的反向引用, 因此可以通过 Tag Model 的 project_tag 字段访问到 ProjectTag Model.project_tag__project_id 表示 ProjectTag Model 的 project_id 字段
Tag.objects.filter(user_tag__user_id=user_id)
UserTag 表的 tag 字段外键到了 Tag 表的 id 字段, 并且定义了 related_name='user_tag'的反向引用. 同时 UserTag 表的 user 字段外键到了 User 表, 因此 user_tag__user_id 表示 User 的 id 字段
总之, 外键的反向引用用两横
- Update
- Tag.objects.filter(id__in=ids).all().update(**update_data)
filter(id__in=ids)相当于 where id in ids
如果过滤的结果是空集则不会执行更新
update_data 是一个字典
利用 Q 构建复杂的查询条件
如何取数据表最后两条数据
Record.objects.order_by('-id')[:2]
[:2]会被翻译为 LIMIT 2
关于这个语句的性能消耗: http://blog.jobbole.com/52852/ 总之就是消耗不大
获取指定列的数据
values: 返回一个 dict
- record = Record.objects.values('id','name').first()
- print(type(record))
- # <class 'dict'>
values_list: 返回一个 tuple, 设置 flat=True 可以在只选择一列的情况下返回不用 tuple 包裹的数据
- # 取最后两条数据记录的 SVN 版本
- ClientVersion.objects.values_list('svn_version', flat=True).order_by('-id')[:2]
若是过滤出了多行数据, 返回的是 queryset 类型, 可以用 list()将其转为列表
获取上一条数据和下一条数据
- # 本条
- obj = Record.objects.get(name='test')
- # 上一条
- pre_obj = Record.objects.filter(id__lt=obj.id).last()
- # 下一条
- next_obj = Record.objects.filter(id__gt=obj.id).first()
不等于
- User.objects.exclude(age=10) // 查询年龄不为 10 的用户
- User.objects.exclude(age__in=[10, 20]) // 查询年龄不为在 [10, 20] 的用户
- exact
- def test_exact():
- query1 = Origin.objects.filter(origin_str='test')
- print(query1.query)
- query2 = Origin.objects.filter(origin_str__exact='test')
- print(query2.query)
- # 二者翻译成 sql 语句是一样的
- # WHERE `translate_app_origin`.`origin_str` = test
筛选空
django model 从数据库中取字符串的时候会自动去掉字符实际内容两旁的空格
比如 queryset.filter(result='') 可以过滤出 result=" "和 result="" 的条目
- # 排除 result=null,result="",result=" "# 注意不要写成 queryset.exclude(result__isnull=True, result=''), 这表示同时满足两个条件才会被过滤
- items = queryset.exclude(result__isnull=True).exclude(result='')
queryset 的拼接
- a1 = User.objects.filter(id__gt=8)
- a2 = User.objects.filter(id__lt=4)
- a3 = a1 | a2
- # 这种方式合并的结构还是一个 queryset, 相当于 a3 把 a1 和 a2 的条件合并了
- # 只能合并同一个数据库同种 model 对象的数据, 并不能拼接两个不同数据库相同 model 的 queryset
- from itertools import chain
- a1 = User.objects.filter(id__gt=8)
- a2 = User.objects.filter(id__lt=4)
- a3 = chain(a1, a2)
- # 这时候 a3 是个可迭代对象, 把 a1 和 a2 分别求出来之后合并成了一个可迭代对象,
- # 可以把不同 model 的对象合并, 类似于与 list 相加.
- # 但是这样合并之后 a3 并不是一个 queryset, 不能用任何筛选, 没什么意义, 还不如全部转成 data_dict 再拼接
总之就是, 没有把多个不同数据库中相同 model 过滤出来的 queryset 合并的办法
distinct
如果出现错误: DISTINCT ON fields is not supported by this database backend
如果你用的 MySQL 数据库, 那么 distinct() 里面不要任何参数, 参数应该写在 value 中去, 如
- language_list = items.values_list('language', flat=True).distinct()
- order by
一个 query 只能有一个 order_by, 如果有多个, 后面的 order_by 会覆盖前面的, 如
- Order.objects.order_by('project_id').order_by('name')
- # sql:
- # select * from order order by order.name ASC
对 bool 值按默认顺序排序的时候, False 会排在 True 前面, 因为 False 相当于 0,True 相当于 1
- # 需要将 True 排在前面
- def test_order_by():
- result = OrderLanguagePair.objects.order_by('-activate').first()
- print(result.activate)
- group by
比如现在想知道每个项目有多少个订单, 在 sql 语句中应对订单按项目 id 分组, 然后求出每组订单的数量
SELECT project_id, count(*) FROM order group by project_id;
django ORM 中没有显式的 group by 函数, 通过 annotate 来实现分组
- # annotate 的作用是为一个 query 增加一个自定义的新字段
- # annotate 接收表达式作为参数
- def annotate(self, *args, **kwargs):
- """
- Return a query set in which the returned objects have been annotated
- with extra data or aggregations.
- """
如果没有指定任何字段, annotate 会根据前面 queryset 的第一个字段 (一般是 id) 分组计算, 如
- Order.objects.annotate(Count('name'))
- # sql:
- # select *, count(order.name) from order group by order.id
在 annotate 前用 values 或 values_list 指定根据什么字段分组, 如
- # 注意 values 要放在 annotate 之前
- Order.objects.values('project_id').annotate(count=Count('*'))
- # sql:
- # select order.project_id, count(*) as count from order group by order.project_id
annotate 定义的字段会加到前面的 values 或 values_list 中
values 中有多个值时, 会按照顺序 group by
- Order.objects.values('project_id', 'name').annotate(count=Count('*'))
- # sql:
- # select order.project_id, order.name, count(*) as count from order group by order.project_id, order.name
如果 annotate 所属的 query 含有 order_by 的话, 除了按 values 的字段分组外, 还会额外按照 order_by 的字段分组(如果 order_by 中的字段不在 values 中)
- # 下面两个 query 对应的 sql 是一样的
- Order.objects.values('project_id').annotate(count=Count('*')).order_by('name')
- Order.objects.order_by('name').values('project_id').annotate(count=Count('*'))
- # sql:
- # select order.project_id, count(*) as count from order
- # group by order.project_id, order.name
- # order by order.name
解决的方法是用对分组字段的排序覆盖 query 之前的排序, 比如
- query = Order.objects.order_by('name')
- query.order_by('project_id').values('project_id').annotate(count=Count('*'))
别名
希望使用 ORM 实现给字段加别名, 如
- select name as user_name, id as user_id
- from users
Django 有两种实现方式
- extra
- User.objects.extra(select={
- 'user_id':user, 'user_name':id
- }). \
- values('user_id', 'user_name')
但是这种方法只能适用于没有外键引用的情况, 即只能选择给此 Model 的字段取别名, 如果要给外键引用的字段取别名, 需要用到下面这种方式
- annotate
- ProjectLanguagePair.objects.\
- annotate(supplier_name=F('supplier__supplier_name')). \
- values('supplier_name')
ProjectLanguagePair 用 supplier 字段外键到了 Supplier 表, 相当于
SELECT `supplier_app_supplier`.`supplier_name` AS `supplier_name`
来源: https://www.cnblogs.com/luozx207/p/11545057.html