常用方法
- all()
查询所有结果 - filter()
筛选满足所给条件的对象,若不存在不会报错 - get()
筛选满足所给条件的对象,但返回结果只有一个。如果返回对象超过一个或没有都会报错 - exclude()
不满足筛选条件的对象。例如 exclude(id=5) 返回那些 id 不等于 5 的对象 - values()
返回一个 QuerySet 得到一个字典,字段:值 - values_list()
和 values 一样,只不过返回一个元组序列,只有 值 - reverse()
对结果进行反向排序,只有在模型定义了 ordering 或使用了 order_by() 方法才可以使用 - distinct()
从返回结果中删除重复记录 - count()
返回匹配条件的数量 - first()
返回第一条记录 - last()
返回最后一条记录 - exists()
如果 QuerySet 包含数据,返回 True,否则返回 Flase - defer()
用来排除某些字段,避免将其转换为 Python 对象 - only
仅选择需要的字段,但是还是需要查询 id
单表查询
from models import Book
# Books 数据中 3 < id < 7 的数据
Book.objects.filter(id__lt=7, id__gt=3)
# Books 数据中 id 等于 [12, 34, 65] 的数据
Book.objects.filter(id__in=[12, 34, 65])
# Books 数据中 id 不等于 [12, 34, 65] 的数据
Book.objects.exclude(id__in=[12, 34, 65])
# Books 数据中 id 在 10-23 的数据,等价于 between and
Books.objects.filter(id__range=[10, 23])
# Books 数据中 name 包含 'test' 的数据
# icontains 表示大小写不敏感,一般用于英文
Book.objects.exclude(name__contains='test')
# 关于字符串还有
startswith,istartswith, endswith, iendswith
# 查找 时间字段
Book.objects.filter(created_time__year=1998)
# 还包括 __month __day
外键查找
正向查找 .属性即可找到相关的对象
反向查找 obj.表名set 查到的是对象,若要查找属性 小写表名_属性
如果在外键中设置了
related_name = "
,那么直接可以使用此" name
。用来代替<表名>_set
。注:一对一反向查询的时候不需要使用 _set,因为只可能取到一个对象。
- 利用 双下划线 跨表查询具体值
查询 book id 是 1 的出版社的名字,sql 语句是执行 inner join 操作
models.Book.objects.filter(id=1).values("publisher__name")
返回字典
models.Book.objects.filter(id=1).values_list("publisher__name")
返回元组
注意只有得到的是一个 QuerySet 对象时才能 values,values_list。如果得到的是一个具体对象,则只能查看其属性。
基于对象查找时使用 _set,在 sql 中对应子查询,就是两条 sql 语句。一般是多条 orm 语句,对象。
基于 QuerySet 查询的时候,不用加 set,采用双下划线,在 sql 中类似于 join 操作。查询出来的 queryset 对象有一个属性 query 查看对应的 sql 语句。一般是一条 orm 语句,后面常跟 values()
多对多
正向查找和外键查找类似
例子:作者和书 多对多 关系
# 通过作者创建一本书
author_obj.books.create()
还有 .add()
添加多个的时候需要使用 * 号表达式,还可以直接添加 id
set() 更新对象 remove 删除关联的对象
clear() 删除关联的所有对象
聚合查询和分组查询
- 聚合
aggregate()
from django.db.models import Avg, Sum, Max, Min, Count
这就是 sql 那些内置函数
查询所有数据的一个函数
例子,一个 Book 的表,现在要求它的平均价格
ret = Books.objects.all().aggregate(price_avg=Avg("price"))
sql 语句:select avg(price) as price__avg from book
- 分组
annotate()
对应 sql 中的 group by 操作
sql:
select Count(1) from xxx group by field
orm:
models.xxx.objects.values('field').annotate(Count('id'))
F 和 Q()
对两个字段的值做比较可以使用 F()
Django 支持 F() 对象和常数之间的数学操作,修改 char 字段时,可以使用 from django.db.models.functions import Concat
如果要执行 or 操作,可以使用 Q() 查询
事务
from django.db import transaction
try:
from django.db import transaction
with transaction.atomic():
pass
except Exception as e:
print(str(e))
- ps 什么时候用 一对一?
当 一张表 的某一些字段查询的时候比较频繁,另外一些字段查询的不是特别频繁时候,把不怎么常用的字段单独拿出来做成一张表,然后用一对一关联起来
优势:既保证数据的完整性,又能较快查找
ManyToMany 的特点
当在一个模型中写 ManyToMany 时,Django 会自动创建第三张表。
当然也可以自己创建第三张表,创建一个新模型里面写两个外键即可,不过这样取数据的时候比较麻烦,因为要显示地通过第三张表来查询。
还有一种方法就是在自己创建的第三张表时使用 ManyToMany。可以在模型的 ManyToMany() 属性中设置 through=<你要通过哪张表关联>,through_fields=(,)。自己创建第三张表时,需要指定一个联合唯一索引,但是这样会失去 ORM 封装的部分特性例如 add 和 remove 方法。联合唯一索引例如
class Meta:
unique_together = ("字段名", "字段名")
如果第三张表没有额外的字段,就用 Django 默认提供的,如果又额外的字段,则可以自己建表。
其他
- select_related()
- 主要针对一对一和外键关系进行优化
- 会对表之间进行 join 连表操作,通过减少 SQL 查询的次数来优化
- 可以通过可变长参数指定需要 select_related 的字段名,也可以通过使用双下划线 __ 连接字段名来实现递归查询。没有指定的字段不会缓存
- 可以通过 depth 参数指定递归的深度,Django 会进行缓存
- 在 1.7 以前,只会保留最后一个链式调用,1.8 之后,就可以链式调用了。
- prefetch_related()
- 对于 多对多和外键进行优化。
- 它会查询每个表,然后用 Python 处理它们之间的关系。在 SQL 中会使用 in
- 可以指定 preftech_related 的字段名,与 selected_related 类似。
- 批量创建 bulk_create
创建好一个 obj 的列表之后就可以进行
models.A.objects.bulk_create(obj_list)
Django 还有另外一种表结构 contenttype
主要用来一张表同时和多张表关联。
例如:如果现在有一张普通课程表,但是还有一张vip课程表。如果要做价格策略,可以按照整个课程做,也可以按照 vi p卖,这样策略必然不同。可以在策略表中添加两个字段:一个是课程,一个是vip,对应的里面填写课程/vip课程的 id,否则填 0 即可。但这样在后期需要添加另一张表需要和价格策略做匹配的时候,还需要添加字段,不方便。Django 提供了在表中添加表名字段的方法,即表名称和 id。
数据库
Django 可以配置多个数据库,借此实现读写分离
在 settings.py
中设置 DATABASES = {}
,拷贝上一份即可,改动某些参数。
然后再执行 python manage.py migrate
的时候,后面可以跟上参数,以指定保存的数据库,--database 数据库名称
。
在查询时,使用 .using(db_name)
来查询,当然了也可以提前指定,新建脚本 myrouter.py
:
class Router:
def db_for_read(self, model, **hints):
return db_name
def db_for_write(self, model, **hints):
return 'default'
在settings.py
中指定 DATABASE_ROUTERS = ['myrouter.Router',]
当然了,也可以指定 app。
class Router:
def db_for_read(self, model, **hints):
if model._meta.app_label == 'app01':
return 'db1'
if model._meta.app_label == 'app02':
return 'db2'
def db_for_write(self, model, **hints):
if model._meta.app_label == 'app01':
return 'db1'
if model._meta.app_label == 'app02':
return 'db2'
详情见官网文档,顺便说一句,Django 的文档是真的好。