目录
3. Error:'flat' is not valid when values_list is called with more than one field.
1. values_list 只适合(1)查询大型结构。(2)这个结构不会被后期更新的。
3. values( ) + order_by( ) + distinct( )
4. values_list( ) 与 values 同理(针对order by和distinct的使用)
十六、报错 Object of type 'XXX' is not JSON serializable
一、ORM & CRUD & QuerySet API
ORM 含义
面向对象(Object Relational Mapping)
实际理解:“代码里的 model ” 对应着 “数据库的 table 表”
Django CRUD 四大事务
增 create 删 delete 改 update 查 read
其中,“ORM的CRUD”与“HTTP请求”的对应关系:
Create(C) = POST 建立数据
Retrieve(R) = GET 取得数据
Update(U) = PUT 完全变更、PATCH 部分修改
Delete(D) = DELETE 刪除
QuerySet API
QuerySet:查询数据库,得到的结果是一个集合。
QuerySet API:查询结果集的接口。Django 提供,可代替原生SQL语句,使用ORM操作数据库(增删改查),如filter()。
二、filter(**t)
作用
字典组合的查询条件,便于不同的查询条件使用同样的查询结构。
格式
先组合字典,然后用字典作为查询条件。
test = {'author':'萝卜干', 'bookname':'数据结构'}
res = models.Book.objects.filter(**test).first()
好处
1. 在其他地方一样可以使用字典里的值(减少变量)
2. 可以代入不同的查询条件(比如说传递不同的表栏位)
三、__isnull
作用
查询空值
格式
使用filter(carriername __isnull = True), 而 不能 用filter(A=None)
四、values_list
作用
获取列表值。
flat 属性
1. flat=True
适用查询一个栏位值
book_qst = models.Book.objects.filter(booktype='IT').first().values_list('bookname')
book_list = [i for i in book_qst]
# book_qst 的值: <QuerySet ['数据结构', '数据库原理']>
# book_list 的值: ['数据结构', '数据库原理']
2. flat=False
适用于查询多个栏位值
book_qst = models.Book.objects.filter(booktype='IT').values_list('bookname', 'author', 'place', flat=False)
book_list = [list(i) for i in book_qst]
# book_qst 的值: <QuerySet [('数据结构', '萝卜干1', '有风的地方'), ('数据库原理', '萝卜干2', '梦幻岛')]>
# book_list 的值:[['数据结构', '萝卜干', '有风的地方'], ['数据库原理', '萝卜丝', '梦幻岛']]
3. Error:'flat' is not valid when values_list is called with more than one field.
原因:设置了 flat=True,但查询了多个栏位。
解决:若想查询多个栏位值,则设置 flat=False
注意
1. values_list 只适合(1)查询大型结构。(2)这个结构不会被后期更新的。
2. 若结构会更新,但又需要获取列表,最好使用列表生成式。
原因:
values_list 是根据 queryset 得出的结果,如果 queryset 一旦有更新,则 values_list 的结果会变更。如果使用列表生成式,则 queryset 就算有变动,也不会影响列表生成式所得出的结果。
比如:
bookqry = models.Book.objects.filter(bookname='数据结构')
reslist = bookqry.values_list('bookname', flat='True')
# 改成列表生成式
bookqry = models.Book.objects.filter(bookname='数据结构')
reslist = [i.bookname for i in bookqry]
五、distinct
作用
去重
搭配
1. values( ) + distinct( )
两者同时使用时,不分先后顺序
但要注意:
若distinct为空,values的值作为去重依据。
若distinct有值,distinct中的值作为去重依据。
2. order_by( ) + distinct( )
distinct中一定要有值作为去重依据
3. values( ) + order_by( ) + distinct( )
若order_by和values的值保持一致,且这两者的值就是和去重依据一样,则distinct可以为空。
若不满足上述,则用distinct中的值作为去重依据。
但要注意:
distinct的值也要存在于order_by里面
order_by要把distinct的值放在最前面
4. values_list( ) 与 values 同理(针对order by和distinct的使用)
a = models.Book.objects.filter(bookname__in=['数据结构', '操作系统','计算机组成原理'])\
.values_list('author', flat=True)\
.distinct()
b = models.Book.objects.filter(bookname__in=['数据结构', '操作系统','计算机组成原理'])\
.values_list('id', flat=True)\
.distinct('author')
c = models.Book.objects.filter(bookname__in=['数据结构', '操作系统','计算机组成原理'])\
.values_list('id', flat=True)\
.order_by('author')\
.distinct('author')
print('a=',a,'\n','b=',b,'\n','c=',c,'\n')
# a= <QuerySet ['萝卜干', '小兔纸']>
# b= <QuerySet [12, 13]> # 加上了author的去重限制,则以distinct为准,只是显示的是values的id
# c= <QuerySet [12, 13]> # order by中要有distinct的值
5. set只能针对列表进行去重,且结果为字典。
6. Django的distinct 对应 SQL的写法
select distinct column1
from table
7. distinct & exclude
(1)distinct 是表的全查之后,再进行去重。所以速度比较慢。
(2)若想查询快些,则用 exclude 先排除再进行查询。
这两种方法,前者是需要值,只去重,后者是不需要值,直接排除。需要根据个人需求依照不同情境来使用。
六、get()
作用
查询单笔值。
get() & filter().first()
get() 和 filter().first() 相比,用filter().first()比较好。
原因:get的查询效率比filter().first()低,已经被后期出现的filter().first()所替代。而且,get 的使用前提是:查询filter的结果必须是唯一的。
七、only()+filter()
只获取自己想要查询的栏位(其他栏位不会被加载,加快数据库的查询)
若想查询其他栏位,也可以在 only 查询结果上,直接点其他栏位,不过会重新进行表查询。
八、exclude()
排除某个栏位值的所在记录。
也可以用~Q()进行查询(下面会讲)。
九、Q 对象查询
导入Q对象
from django.db.models import Q
实现 OR、AND 和 NOT 运算
1. 基础用法
# 逗号写法,默认是“and” (age大于23岁且性别是女 且 身高是160cm且体重是50kg)
q1 = {'age__gt': 23, 'sex': '女'}
q2 = {'height__gt': '160cm', 'weight': '50kg'}
models.Author.objects.filter(Q(**q1), Q(**q2))
# |写法,代表是“或” (age大于23且性别是女 或 age大于20且性别是男)
q1 = {'age__gt': 23, 'sex': '女'}
q2 = {'age__gt': 20, 'sex': '男'}
models.Author.objects.filter(Q(**q1) | Q(**q2))
# ~ 写法,代表是“非” (age不等于23)
# 类似于Django ORM 的 exclude 查询
models.Author.objects.filter(~Q(age=23))
# ~ 写法(搭配&),代表是“前者满足,并且后者是非” (age大于23 且 age不小于56)
models.Author.objects.filter(Q(age__gt=23) &~ Q(age__gt=56))
2. 高级用法(动态组成查询条件)
# ====================第一种写法====================
# 导入Django Q
from django.db.models import Q
# 先定义一个 Q 对象
q = Q()
# 用 Q 对象组装查询条件
for i in [{'bookname': '数据结构', 'bookprice': '¥100'}, {'bookname': '操作系统', 'bookprice': '¥200'}]:
i = { 'book__name': i['bookname'],'book__price': i['bookprice'] }
q |= Q(**i)
# 查询书籍是数据结构且价格是100元的、书籍是操作系统且价格是200元数据库数据
models.Author.objects.filter(q)
# ====================第二种写法====================
# 导入Django Q
from django.db.models import Q
# 先定义一个 Q 对象
con = Q()
con.connector = 'OR'
# 用 Q 对象组装查询条件
for i in [{'bookname': '数据结构', 'bookprice': '¥100'}, {'bookname': '操作系统', 'bookprice': '¥200'}]:
con.children.append(Q(book__name=i['bookname']) & Q(book__price=i['bookprice']))
# 查询书籍是数据结构且价格是100元的、书籍是操作系统且价格是200元数据库数据
models.Author.objects.filter(con)
# ====================第三种写法====================
# 导入Django Q、operator、reduce
from django.db.models import Q
import operator
from functools import reduce
# 先定义查询条件
filterCond = []
# 用 Q 对象组装查询条件
for i in [{'bookname': '数据结构', 'bookprice': '¥100'}, {'bookname': '操作系统', 'bookprice': '¥200'}]::
filterCond.append(Q(book__name=i['bookname'], book__price=i['bookprice']))
# 查询书籍是数据结构且价格是100元的、书籍是操作系统且价格是200元数据库数据
cmodels.Book.objects.filter(reduce(operator.or_, filterCond))
十、Iterator
含义
保持连接查询。
作用
防止产生cache,节省内存
缺点
只能用一次,一定要避免重复执行查询
注意
iterator 不能结合 first( )
用法
1. 结合object,遍历取值
res = models.Book.objects.filter(id__in=[1,2,3]).iterator()
for i in res:
print(i.bookname)
2. 结合字典,遍历取值
res = models.Book.objects.filter(id__in=[1,2,3]).values('bookname').iterator()
for i in res:
print(i['bookname'])
十一、aggregate
含义
进行聚合操作。
作用
函数里进行计算和操作结果集中的字段。结果是一个字典。
用法
# 例如:计算author为萝卜干的总数,并赋予一个字段存起来。
from django.db.models import Count
# 1.使用默认的id加上__count作为字典的key
res=models.Book.objects.filter(author='萝卜干').aggregate(Count('id'))
# res的结果 {'id__count': 3}
# 2.自定义字典的key
res=models.Book.objects.filter(author='萝卜干').aggregate(totalnum=Count('id'))
# res的结果 {'totalnum': 3}
聚合操作有:计算 和Sum、平均数Avg、最大值Max、最小值Min、计数Count 等
十二、annotate
作用
分组查询。
用法
# 例如:以书籍类型进行分组,分别计算author为萝卜干的总数,并赋予一个字段存起来。
from django.db.models import Count
res=models.Book.objects.filter(author='萝卜干').values('booktype').annotate(totalnum=Count('id'))
# res的结果 <QuerySet [{'booktype': '悬疑', 'totalnum': 2}, {'booktype': '爱情', 'totalnum': 1}]>
注意
values('booktype'):分组依据 (相当于 SQL 中的group by)
annotate(totalnum=Count('id')):要查询的内容
顺序很重要:如果在annotate之前调用values失败,不会产生查询结果。
搭配count的去重属性
annotate在结合count计数时,可以使用count的distinct属性,进行去重。
# 例如:以书籍类型进行分组,分别计算author为萝卜干的总数,并赋予一个字段存起来。
# 需求:使用bookname计数时,相同的书名只计数一次
from django.db.models import Count
res=models.Book.objects.filter(author='萝卜干').values('booktype').annotate(totalnum=Count('bookname', distinct=True))
# res的结果 <QuerySet [{'booktype': '悬疑', 'totalnum': 2}, {'booktype': '文学', 'totalnum': 1}]>
十三、迭代Django查询集 与 迭代变量
无论是“直接迭代Django查询集”,还是“先将Django查询集放在变量,再迭代变量“,针对python,无任何区别,在 CPython 中,所有数据都存储在堆上,而堆栈包含对该数据的引用。
但需注意:是否多次引用查询集。如果是,则将其存在变量中;否则,可以不用变量存储。
十四、主表和外键表的查询
两张表的models如下:
# 主表Book的model
class Book(models.Model):
authorid = models.ForeignKey(Author, related_name="authorId", on_delete=models.CASCADE, limit_choices_to={"active": True}, verbose_name=_("authorName"))
booktype = models.CharField(verbose_name=_("BookType"), max_length=40)
# 外键表Author的model
class Author(models.Model):
name = models.CharField(verbose_name=_("authorName"), max_length=40)
age = models.IntegerField(default=0)
(一)已知主表
1. 举例理解
已知主表 Book 的 booktype 为'悬疑',外键是 author (外键所在表是 Author ),查询外键表的age。
2. 有两种方式
(1)正向查询
book_qry = models.Book.objects.filter(booktype='悬疑').values('authorid').first()['authorid']
res = models.Author.objects.filter(id = book_qry).values('age') # <QuerySet [{'age': 23}]>
(2)正向外键查询 (factory是related_name设定的)
# 原理是:Book表 透过 authorid对象 去导向 FK表 获取该FK表下的name的value
res = models.Book.objects.filter(booktype='悬疑').first().authorId.name # '萝卜干'
(二)已知外表
1. 举例理解
已知外键表Author的name为'萝卜干',查询主表Book的信息。
2. 用法
res = models.Book.objects.filter(authorid__name='萝卜干')
# <QuerySet [<Book: 数据结构>, <Sector: 操作系统>]>
当主表中存在多个外键,可以在主表的外键中设置related_name属性值。
十五、查询结果:Queryset 和 Object
(一)查询时
Queryset没有使用first,Object是需要使用first。
(二)取值方式
Queryset要使用点的形式,Object使用【''】的形式取值。
例1:
filterdata={'author__in': ['萝卜干','小兔纸']}
Queryset是models.Book.filter(**filterdata)的结果 <QuerySet [<Book: Book object (12)>, <Book: Book object (13)>]>
Object是models.Book.filter(**filterdata).first( )的结果 Book object (12)
例2:
a=models.Book.objects.filter(authorname='萝卜干')
# <QuerySet [<Book: Book object (67)>, <Book: Book object (69)>]>
b=models.Book.objects.filter(authorname='萝卜干').first()
# Book object (67)
c=models.Book.objects.filter(authorname='萝卜干').values('booktype')
# <QuerySet [{'booktype': '悬疑'}, {'booktype': '爱情'}]>
d=models.Book.objects.filter(authorname='萝卜干').values('booktype').first()
# {'booktype': '悬疑'}
e=models.Book.objects.filter(authorname='萝卜干').values('booktype').distinct()
# <QuerySet [{'booktype': '悬疑'}, {'booktype': '爱情'}]>
f=models.Book.objects.filter(authorname='萝卜干').values_list('booktype')
# <QuerySet [('悬疑',), ('爱情',)]>
g=models.Book.objects.filter(authorname='萝卜干').values_list('booktype', flat=True)
# <QuerySet ['悬疑', '爱情']>
十六、报错 Object of type 'XXX' is not JSON serializable
第一种:查询外键的值
1. 原因
当报错 Object of type 'BookType' is not JSON serializable,可能是查询外键栏位的值,是一个queryset格式,将一个非JSON的格式用return JsonResponse( ) 后,会报错。
2. 分析
查询涉及到 BookType 和 Book 两张表。
class Book(models.Model):
name=models.CharField(max_length=40, primary_key=True, db_index=True)
class BookType(models.Model):
type=models.ForeignKey(Book, on_delete=models.CASCADE)
当通过BookType表查询type的值时,会关联到Book表。但是res中的type值是一个关联外键的表的值,是一个queryset格式,所以还需要点到外表的name值,即“结果.栏位名”。
比如:
res = cmodels.BookType.objects.first()
res_name = res.name
还有一种取值情况:
在取值的时候就直接取字典,而不是queryset的结果。比如,若用getattr取值的情况,就直接将外键的查询栏位名写的和数据库的栏位名一致。假设BookType表的type栏位对应的数据库表自动生成的栏位名是type_id。
res = cmodels.BookType.objects.first()
res_name = getattr(res, 'type_id')
第二种:查询ManyToManyField 的值
当报错 Object of type 'ProductSpec' is not JSON serializable
原因:ManyToManyField 不能用 model_to_dict。
十七、原生SQL查询
一般情况下使用ORM,方便代码统一调整管理。当ORM实在没办法实现查询时,才用原生SQL。
这是python的写法,不能放进数据库中查询。
SQL 和 原生SQL 还是有区别的。
例子:
1.数据库查询
color_list = ['blue', 'red']
cond = ''
for i in color_list:
cond += (' OR CASE WHEN '
' coverColor = \'{}\' '
' AND fontColor = \'{}\' '
' THEN 0 ELSE 1 END=0 ').format(i, 'black')
sql = 'SELECT id FROM {} WHERE 1=0 {}'.format('public.\"Book\" ', cond)
# 组合的sql结果:
# SELECT id FROM public."Book"
# WHERE 1=0
# OR CASE WHEN coverColor = 'blue' AND fontColor = 'black' THEN 0 ELSE 1 END=0
# OR CASE WHEN coverColor = 'red' AND fontColor = 'black' THEN 0 ELSE 1 END=0
2.生成对象,装入列表
objList = [i for i in models.Book.objects.raw(sql)]
则可以通过 objList 得到数据库的值。
官方文档:https://docs.djangoproject.com/zh-hans/4.2/topics/db/sql/
十八、select_related & prefetch_related
(一)select_related
1.可反复使用,不会被释放。
2.反复使用该查询结果时,只会查询一次数据库(大大减少SQL查询的次数)。
比如:for循环一个queryset,该queryset是一笔死数据,不会重新查询数据库。
3.必须是外键。
4.针对一对一、一对多的关系。
5.适用于:针对外键查询,查询结果会被反复使用。
(二)prefetch_related
1.是针对 ManyToMany (多对多)。
2.效率
prefetch_related with object.object= 0.025872241
prefetch_related with object.object and only = 0.030737298
prefetch_related with completely relation = 0.027219513
prefetch_related with completely relation and only = 0.025871288
select_related with object.object= 0.017184442
select_related with object.object and only = 0.01938707
select_related with completely relation = 0.008505491
select_related with completely relation and only = 0.008488287
values with completely relation = 0.00621382