Backend - Django ORM 性能优化(Read 查询)

目录

一、ORM & CRUD & QuerySet API

ORM 含义

Django CRUD 四大事务

QuerySet API

二、filter(**t) 

作用

格式

好处

三、__isnull 

作用

格式

四、values_list

作用

flat 属性

1. flat=True

2. flat=False

3. Error:'flat' is not valid when values_list is called with more than one field.

注意

1. values_list 只适合(1)查询大型结构。(2)这个结构不会被后期更新的。

2. 若结构会更新,但又需要获取列表,最好使用列表生成式。

五、distinct

作用

搭配

1. values( )  + distinct( )

2. order_by( ) + distinct( )

3. values( ) + order_by( ) + distinct( )

4. values_list( ) 与 values 同理(针对order by和distinct的使用)

5. set只能针对列表进行去重,且结果为字典。

6. Django的distinct 对应 SQL的写法

7. distinct & exclude

六、get()

作用

get() & filter().first()

七、only()+filter()

八、exclude()

九、Q 对象查询

导入Q对象

实现 OR、AND 和 NOT 运算

1. 基础用法

2. 高级用法(动态组成查询条件)

十、Iterator

含义

作用

缺点

注意

用法

1. 结合object,遍历取值

2. 结合字典,遍历取值 

十一、aggregate

含义

作用

用法 

十二、annotate

作用

用法

注意

搭配count的去重属性

十三、迭代Django查询集 与 迭代变量

十四、主表和外键表的查询

(一)已知主表

1. 举例理解

2. 有两种方式

(1)正向查询

(2)正向外键查询 (factory是related_name设定的)

(二)已知外表

1. 举例理解

2. 用法

十五、查询结果:Queryset 和 Object

(一)查询时

(二)取值方式

十六、报错 Object of type 'XXX' is not JSON serializable

第一种:查询外键的值

1. 原因

2. 分析

第二种:查询ManyToManyField 的值 

十七、原生SQL查询

1.数据库查询

2.生成对象,装入列表


一、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/

1.可反复使用,不会被释放。

2.反复使用该查询结果时,只会查询一次数据库(大大减少SQL查询的次数)。

比如:for循环一个queryset,该queryset是一笔死数据,不会重新查询数据库。

3.必须是外键。

4.针对一对一、一对多的关系。

5.适用于:针对外键查询,查询结果会被反复使用。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值