Django ORM 知识点总结
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)
在访问两个数据库的时候,需要把对前一个数据库访问的结果转为缓存数据再执行对下一个数据库的访问,比如
# object1与object2通过关系表Relations关联
# object1和Relations表在同一个数据库中,object2在另一个数据库中
# 现在需要通过object1的一堆id来找到对应的object2
# 错误写法:
object2_ids = Relations.filter(object1_id__in=(object1_ids)).values_list('object2_id', flat=True).distinct()
object2s = Object2.objects.filter(id__in=object2_ids)
# 如果这时直接这样写,则实际上是涉及两个数据库的query的拼接,会出错
# 应该将第一个query转换为内存数据list
# 正确写法:
object2_ids = list(Relations.filter(object1_id__in=(object1_ids)).values_list('object2_id', flat=True).distinct())
object2s = Object2.objects.filter(id__in=object2_ids)
多使用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`
Django ORM 知识点总结的更多相关文章
- Django 2.0 学习(14):Django ORM 数据库操作(上)
Django ORM 数据库操作(上) ORM介绍 映射关系: 数据库表名 ---------->类名:数据库字段 ---------->类属性:数据库表一行数据 ----------&g ...
- 关于Django查询知识点总结
========关于Django查询知识点总结======= models.Book.objects.filter(**kwargs): querySet [obj1,obj2] models.Boo ...
- Django框架之第六篇(模型层)--单表查询和必知必会13条、单表查询之双下划线、Django ORM常用字段和参数、关系字段
单表查询 补充一个知识点:在models.py建表是 create_time = models.DateField() 关键字参数: 1.auto_now:每次操作数据,都会自动刷新当前操作的时间 2 ...
- django orm总结[转载]
django orm总结[转载] 转载地址: http://www.cnblogs.com/linjiqin/archive/2014/07/01/3817954.html 目录1.1.1 生成查询1 ...
- Django ORM - 001 - 外键表查询主表信息
开始用Django做web开发,我想大家都会遇到同样的问题,那就是如何高效快速的查询需要的数据,MVC都很简单,但是ORM折腾起来就有些费时间,我准备好好研究下Django ORM,所以会有一个系列的 ...
- Django ORM 中的批量操作
Django ORM 中的批量操作 在Hibenate中,通过批量提交SQL操作,部分地实现了数据库的批量操作.但在Django的ORM中的批量操作却要完美得多,真是一个惊喜. 数据模型定义 首先,定 ...
- Django ORM 查询管理器
Django ORM 查询管理器 ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言 ...
- Django ORM模型的一点体会
作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载. 使用Python的Django模型的话,一般都会用它自带的ORM(Object-relational ma ...
- 数据库表反向生成(二) Django ORM inspectdb
在前一篇我们说了,mybatis-generator反向生成代码. 这里我们开始说如何在django中反向生成mysql model代码. 我们在展示django ORM反向生成之前,我们先说一下怎么 ...
随机推荐
- ecshop 管理后台菜单及权限管理机制
ecshop 所有的一级菜单选项存放于languages\zh_cn\admin\common.php 文件里面,使用 $_LANG['02_cat_and_goods'] = '商品管理'; 这样 ...
- ES 26 - 通过partial update局部更新索引文档 (partial update增量修改原理)
目录 1 什么是partial update 1.1 全量修改文档的原理 1.2 修改指定field的思路 1.3 partial update的优势 1.4 partial update的使用 2 ...
- 二级小兵——工厂模式(Factory Method)
前言 上一篇我们介绍了单例模式,今天给大家讲一个比较简单的模式——工厂模式(Factory Method),工厂模式又是什么呢?顾名思义,工厂——生产制造东西的地方.那么应用在程序当中该如何使用.并且 ...
- MyBatis的flushCache和useCache的使用注意
之前在利用MyBatis做开发的时候,遇到了一个问题,使用select配置的时候发现前后两次的结果是一样的,并且使用statementType="CALLABLE"配置,然后在配置 ...
- c++智能指针介绍
C++11标准引入了boost库中的智能指针,给C++开发时的内存管理提供了极大的方便.接下来这篇文件介绍shared_ptr/weak_ptr内部实现原理及使用细节. C++不像java有内存回收机 ...
- k8s西游记 - 切换网络插件IP池
前言 最近在另一个k8s集群中,搭建了kong网关,在配置OIDC插件时,希望使用Memcahe代替Cookie来存储会话信息,于是把部署在同一局域网Memcahe的内网IP,比如:192.168.1 ...
- .Net Core 三大Redis客户端对比和使用心得
前言 稍微复杂一点的互联网项目,技术选型都可能会涉及Redis,.NetCore的生态越发完善,支持.NetCore的Redis客户端越来越多, 下面三款常见的Redis客户端,相信大家平时或多或少用 ...
- VScode 插件推荐与C/C++配置
以下是我经常用到的VScode插件.由于插件本身具有详细的配置和介绍,不对插件本身的安装配置进行说明,仅仅支出这些插件的主要功能.具体使用强烈推荐看一下安装插件后的说明,大多数的问题和设置都可以找到, ...
- Java IO体系综述
Java IO体系综述 一.流的概念 在Java API中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称作输出流.这些字节序列的来源地和目的地可以是文件,而且通常都 ...
- 基于 Javassist 和 Javaagent 实现动态切面
一.背景介绍 1.需求说明 需求是在程序运行期间,向某个类的某个方法前.后加入某段业务代码,或者直接替换整个方法的业务逻辑,即业务方法客制化.注意是运行期间动态更改,做到无侵入,而不是事先在代码中写死 ...