Django orm高级用法以及查询优化

基础查询

#全表查询
Author.objects.all()
#<QuerySet [<Author: Author object (4)>, <Author: Author object (1)>, <Author: Author object (10)>, <Author: Author object (8)>, <Author: Author object (5)>, <Author: Author object (6)>, <Author: Author object (2)>, <Author: Author object (9)>, <Author: Author object (7)>]>#查出数据库前三条数据
Author.objects.all().order_by('id')[:3]#查询某个字段 数据以列表形式返回,列表元素以字典表示
v=Author.objects.values('name')
#<QuerySet [{'name': '吴承恩'}, {'name': '孙咯'}, {'name': '孙权'}, {'name': '张三'}, {'name': '李四'}, {'name': '林黛玉'}, {'name': '赵咯'}, {'name': '赵武'}]>#查询某个字段 数据以列表形式返回,列表元素以元组表示
v=Author.objects.values_list('name')
#<QuerySet [('吴承恩',), ('孙咯',), ('孙权',), ('张三',), ('李四',), ('林黛玉',), ('赵咯',), ('赵武',)]>#查询特定数据get和filter
v=Author.objects.get(id=2)
#<Author: Author object (2)>
v=Author.objects.filter(id=2)
#[<Author: Author object (2)>]#相当于SQL的and查询
v=Author.objects.filter(id=2,name='林黛玉')
#<QuerySet [<Author: Author object (2)>]>
v=Author.objects.filter(id=2).first()
#<Author: Author object (2)>#SQL中的or查询需要引入Q
from django.db.models import Q
v=Author.objects.filter(Q(id=2)|Q(name='李四'))
#<QuerySet [<Author: Author object (6)>, <Author: Author object (2)>]>#实现不等于查询,在Q前面加一个~,或者使用exclude
Author.objects.filter(~Q(id=2))
Author.objects.exclude(id=2) 
#<QuerySet [<Author: Author object (4)>, <Author: Author object (1)>, <Author: Author object (10)>, <Author: Author object (8)>, <Author: Author object (5)>, <Author: Author object (6)>, <Author: Author object (9)>, <Author: Author object (7)>]>#统计数量
Author.objects.exclude(id=2).count()#去重查询
v=Author.objects.values_list('name').distinct()#聚合查询annotate aggregate
#annotate相当于SQL中的group by
#SQL:select job,SUM(id) as id__sum from index_vocation group by job
Author.objects.values('name').annotate(Sum('id')) 
Author.objects.values('name').annotate(id__sum=Sum('id'))
#<QuerySet [{'name': '吴承恩', 'id__sum': Decimal('1')}, {'name': '林黛玉', 'id__sum': Decimal('2')}, {'name': '何玉梅', 'id__sum': Decimal('4')}, {'name': '张三', 'id__sum': Decimal('5')}, {'name': '李四', 'id__sum': Decimal('6')}, {'name': '赵武', 'id__sum': Decimal('7')}, {'name': '孙权', 'id__sum': Decimal('8')}, {'name': '赵咯', 'id__sum': Decimal('9')}, {'name': '孙咯', 'id__sum': Decimal('10')}]>#aggregate 计算某个字段的值并返回结果
#SQL:select count(id) as id id_count from author
Author.objects.aggregate(id_count=Count('id'))
#{'id_count': 9}# union、intersection 和 difference 语法
# 每次查询结果的字段必须相同
# 第一次查询结果 v1
vl = Vocation.objects.filter(payment__gt=9000)
<QuerySet [<Vocation: 1>, <Vocation: 5>]>
# 第二次查询结果 v2
v2 = Vocation.objects.filter(payment__gt=5000)
<QuerySet [<Vocation: 1>,<Vocation: 3>,<Vocation: 4>,<Vocation: 5>]>
#使用SQL的UNION 来组合两个或多个查询结果的并集#获取两次查询结果的并集
vl.union(v2)
<QuerySet [<Vocation: 1>,<Vocation: 3>,<Vocation: 5>]>
#使用SQL的NTERSECT来获取两个或多个查询结果的交集#获取两次查询结果的交集
vl.intersection(v2)
<QuerySet [<Vocation: 1>, <Vocation: 5>]>?
#使用 SQL的EXCEPT来获取两个或多个查询结果的差
#以v2为目标数据,去除 v1和v2的共同数据v2.difference(v1)
<QuerySet [<Vocation: 3>, <Vocation: 4>]>

orm高级用法

创建表:

# models.py
from django.db import modelsclass Book(models.Model):id = models.AutoField(primary_key=True) # 自增,主键title = models.CharField(verbose_name='书名', max_length=32)price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)publish_date = models.DateField(verbose_name='出版日期',auto_now_add=True) # 年月日类型# 阅读数# reat_num=models.IntegerField(default=0)# 评论数# commit_num=models.IntegerField(default=0)# 建议加引号,也可以不加引号#models.CASCADE:级联删除,设为默认值,设为空,设为指定的值,不做处理# 2.x以后必须加on_dekete,否则报错# publish = models.ForeignKey(to=Publish,to_field='id',on_delete=models.CASCADE)  # 一对多 外键字段建在多的一方publish = models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)# 多对多 必须建立第三张表(orm中,可以用一个字段表示,在数据库中,根本没有这个字段,# orm用来查中介模型询,映射成一个表了,如果我不这么写,则需要手动建立第三张表,)authors = models.ManyToManyField(to='Author')def __str__(self):return self.titleclass Publish(models.Model):id = models.AutoField(primary_key=True)title = models.CharField(verbose_name='名称',max_length=32)addr = models.CharField(verbose_name='地址',max_length=128)email = models.EmailField(verbose_name='邮箱') # 本质是varchar类型class Author(models.Model):id = models.AutoField(primary_key=True)name = models.CharField(verbose_name='姓名',max_length=32)age = models.IntegerField(verbose_name='年龄')# 一对一 外键字段推荐建在查询频率较高的表中author_detail = models.OneToOneField(to='AuthorDetail',to_field='id',unique=True,on_delete=models.CASCADE)class AuthorDetail(models.Model):id = models.AutoField(primary_key=True)phone = models.BigIntegerField(verbose_name='电话')addr = models.CharField(verbose_name='地址',max_length=32)

聚合查询

聚合函数,Sum,Max,Min,Count,Avg
       总数,最大值,最小值,数量,平均值
把聚合结果字段重命名
res=models.Book.objects.all().aggregate(aaa=Sum('price'))

aggregate()

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

# 聚合查询from django.db.models import Sum,Avg,Max,Min,Count
# 计算所有图书的平均价格res=models.Book.objects.all().aggregate(Avg('price'))print(res)
# 计算所有图书的最高价格res=models.Book.objects.all().aggregate(Max('price'))print(res)
# 计算所有图书的总价格res=models.Book.objects.all().aggregate(Sum('price'))print(res)
# pmj出版图书的总价格res = models.Book.objects.filter(authors__name='pmj').aggregate(Sum('price'))print(res)
# 北京的出版社出版的书的最高价格res = models.Book.objects.filter(publish__addr='北京').aggregate(Sum('price'))print(res)
# 计算所有图书的总价格res=models.Book.objects.all().aggregate(book_sum=Sum('price'),book_avg=Avg('price'))print(res)

分组查询

annotate()

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所以使用前要先从 django.db.models 引入 Avg,Max,Min,Count,Sum(首字母大写))。

返回值:

  • 分组后,用 values 取值,则返回值是 QuerySet 数据类型里面为一个个字典
  • 分组后,用 values_list 取值,则返回值是 QuerySet 数据类型里面为一个个元组

MySQL 中的 limit 相当于 ORM 中的 QuerySet 数据类型的切片

注意:

annotate ()里面放聚合函数。

  • values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
  • values 或者 values_list 放在 annotate 后面:** **annotate****表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。
  • filter放在 annotate 前面:表示where条件
  • filter放在annotate后面:表示having

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。

import osos.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
if __name__ == '__main__':import djangodjango.setup()from app01 import models# 查询每一个出版社id,以及出书平均价格(单表)# 原生sql# select publish_id,avg(price) from book group by publish_id;# orm实现'''标准 annotate() 内写聚合函数values在前,表示group by 的字段values在后,表示取字段filter在前,表示where条件filter在后,表示having'''from django.db.models import Avg,Count,Max# 查询每一个出版社id,以及出书平均价格(单表)res = models.Book.objects.all().values('publish_id').annotate(price_ave=Avg('price')).values('publish_id','price_ave')print(res)# 查询出版社id大于1的出版社id,以及出书平均价格res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(price_ave=Avg('price')).values('publish_id','price_ave')print(res)# 查询出版社id大于1的出版社id,以及出书平均价格大于30的res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(price_ave=Avg('price')).filter(price_ave__gt=60).values('publish_id','price_ave')print(res)# 查询每一个出版社出版的名称和书籍个数(连表)# 联表的话最好以group by的表作为基表res = models.Publish.objects.values('nid').annotate(book_count=Count('book__nid')).values('name','book_count')# 简写成,如果基表是group by的表,就可以不写valuesres=models.Publish.objects.annotate(book_count=Count('book')).values('name','book_count')# 以book为基表res = models.Book.objects.values('publish__nid').annotate(book_count=Count('nid')).values('publish__name','book_count')print(res)# 查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)# 多对多如果不以分组表作为基表,可能会出数据问题res = models.Author.objects.annotate(price_max=Max('book__price')).values('name','price_max')res = models.Book.objects.values('authors__nid').annotate(price_max=Max('price')).values('authors__name','price_max')print(res)# 查询每一个书籍的名称,以及对应的作者个数res=models.Book.objects.annotate(count=Count('authors')).values('name','count')print(res)# 统计不止一个作者的图书ret = models.Author.objects.values('book__id').annotate(count=Count('id')).filter(count__gt=1).values('book__name', 'count')print(ret)# 统计价格数大于10元,作者的图书ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).values('name',
'count')print(ret)#统计价格数大于10元,作者个数大于1的图书res = models.Book.objects.filter(price__gt=10).annotate(count=Count('authors')).filter(count__gt=1).values('name','price','count')print(res)

annotate()高级用法

  1. F(): 使用字段的值参与计算。F()对象允许在查询中引用模型字段的值,而不仅仅是其名称。这对于在聚合函数中进行字段间的运算非常有用。

    from django.db.models import Fqueryset.annotate(new_stock=F('stock') + F('incoming_stock'))
  2. ExpressionWrapper(): 创建一个自定义的表达式,并指定其输出字段类型。常用于组合多个表达式或调整现有表达式的输出类型。

    from django.db.models import ExpressionWrapper, FloatFieldqueryset.annotate(discount_price=ExpressionWrapper(F('original_price') * (1 - F('discount_percentage') / 100),output_field=FloatField())
    )
  3. Case()When()Value()Q(): 构造复杂的条件判断和基于条件的计算。Case()用于创建一个分支结构,When()用于定义一个条件分支及其结果值,Value()用于插入一个常数值,Q()用于构建布尔表达式。

    from django.db.models import Case, When, Value, Qqueryset.annotate(status_label=Case(When(Q(status='active') & Q(date_expired__gte=datetime.now()), then=Value('Active')),When(Q(status='inactive'), then=Value('Inactive')),default=Value('Unknown'),output_field=CharField())
    )

F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

F查询
当查询条件的左右两边数据都来自于表中则可以使用F查询
F查询甚至还可以统一修改表中字段数据

针对字符串稍微复杂一点需要再导入两个模块
from django.db.models import F,Value
from django.db.models.functions import Concat

# F 查询,取出某个字段对应的值from django.db.models import F# 查询评论数大于阅读数的书籍res=models.Book.objects.filter(commit_num__gt=F('read_num'))print(res)# 把所有图书价格+1res=models.Book.objects.all().update(price=F('price')+1)print(res) # 影响的行数# 把egon出版的所有图书价格加10res = models.Book.objects.filter(authors__name='egon').update(price=F('price')+1)print(res)# 将所有书的后面加上爆款两个字from django.db.models.functions import Concatfrom django.db.models import Value# res = models.Book.objects.update(title = Concat(F('title'),Value('爆款')))

Q查询

filter()等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。

Q查询
1.filter默认只支持and连接查询条件如果需要修改则需要使用Q
导入模块:from django.db.models import Q
filter(Q(),Q()) # and
filter(Q()|Q()) # or
filter(Q(),Q()) # not
2.查询条件还可以由变量名模式改为字符串形式
q = Q()
q.connector='or'
q.children.append(('字段名','条件'))
res = models.Book.objects.filter(q)


# 1、 Q查询:构造出  与 &    或 |   非 ~from django.db.models import Q# 查询名字叫红楼梦或者价格大于100的书res=models.Book.objects.filter(Q(name='红楼梦')|Q(price__gt=100))res=models.Book.objects.filter(Q(name='红楼梦')& Q(price__gt=100))# 查询名字不是红楼梦的书res=models.Book.objects.filter(~Q(name='红楼梦'))# 查询名字不是红楼梦,并且价格大于100的书# res = models.Book.objects.filter(~Q(name='红楼梦'),price__gt='100')res = models.Book.objects.filter(~Q(name='红楼梦')&Q(price__gt='100'))print(res)2、进阶操作:# condition = input('请输入你需要按照什么字段查询数据>>>:')+'__contains'# data = input('请输入你需要查询的数据名称>>>:')# q = Q()# q.children.append((condition,data))# # print(q,type(q))# res = models.Book.objects.filter(q)# print(res)

orm查询优化

什么是QuerySet

QuerySet是Django提供的强大的数据库接口(API)。正是因为通过它,我们可以使用filter, exclude, get等方法进行数据库查询,而不需要使用原始的SQL语言与数据库进行交互。从数据库中查询出来的结果一般是一个集合,这个集合叫就做 queryset。

Django的QuerySet是惰性的。

下例中article_list试图从数据库查询一个标题含有django的全部文章列表。

article_list = Article.objects.filter(title__contains="django")

但是当我们定义article_list的时候,Django的数据接口QuerySet并没有对数据库进行任何查询。无论你加多少过滤条件,Django都不会对数据库进行查询。只有当你需要对article_list做进一步运算时(比如打印出查询结果,判断是否存在,统计查询结果长度),Django才会真正执行对数据库的查询(见下例1)。这个过程被称为queryset的执行(evaluation)。Django这样设计的本意是尽量减少对数据库的无效操作,比如查询了结果而不用是计算资源的很大浪费。

# example 1
for article in article_list:print(article.title)

Django的QuerySet自带缓存(Cache)

在例1中,当你遍历queryset(article_list)时,所有匹配的记录会从数据库获取。这些结果会载入内存并保存在queryset内置的cache中。这样如果你再次遍历或读取这个article_list时,Django就不需要重复查询了,这样也可以减少对数据库的查询。

下例中例2比例3要好,因为在你打印文章标题后,Django不仅执行了查询,还把查询到的article_list放在了缓存里。这个article_list是可以复用的。例3就不行了。

# Example 2: Good
article_list = Article.objects.filter(title__contains="django")
for article in article_list:print(article.title)# Example 3: Bad
for article in Article.objects.filter(title__contains="django"):print(article.title)

用if也会导致queryset的执行

不知道你注意到上述例2中有个问题没有?万一article_list是个空数据集呢? 虽然for....in...用到空集合上也不会出现raise什么错误,但专业优秀的我们怎么能允许这样的低级事情发生呢?最好的做法就是在loop前加个if判断(例4)。因为django会对执行过的queryset进行缓存(if也会导致queryset执行, 缓存article_list),所以我们在遍历article_list时不用担心Django会对数据库进行二次查询。

# Example 4: Good
article_list = Article.objects.filter(title__contains="django")
if article_list:for article in article_list:print(article.title)
else:print("No records")

但有时我们只希望了解查询的结果是否存在,而不需要使用整个数据集,这时if触发整个queryset的缓存变成了一件坏事情。哎,程序员要担心的事情着不少。这时你可以用exists()方法。与if判断不同,exists只会检查查询结果是否存在,返回True或False,而不会缓存article_list(见例5)。

# Example 5: Good
article_list = Article.objects.filter(title__contains="django")
if article_list.exists():print("Records found.")
else:print("No records")

注意: 判断查询结果是否存在到底用if还是exists取决于你是否希望缓存查询数据集复用,如果是用if,反之用exists。

统计查询结果数量优选count方法

len()与count()均能统计查询结果的数量。一般来说count更快,因为它是从数据库层面直接获取查询结果的数量,而不是返回整个数据集,而len会导致queryset的执行,需要将整个queryset载入内存后才能统计其长度。但事情也没有绝对,如果数据集queryset已经在缓存里了,使用len更快,因为它不需要跟数据库再次打交道。

下面三个例子中,只有例7最差,尽量不要用。

# Example 6: Good
count = Article.objects.filter(title__contains="django").count()# Example 7:Bad
count = Article.objects.filter(title__contains="django").len()# Example 8: Good
article_list = Article.objects.filter(title__contains="django")
if article_list:print("{} records found.".format(article_list.len()))

当queryset非常大时,数据请按需去取

当查询到的queryset的非常大时,会大量占用内存(缓存)。我们可以使用values和value_list方法按需提取数据。比如例1中我们只需要打印文章标题,这时我们完全没有必要把每篇文章对象的全部信息都提取出来载入到内存中。我们可以做如下改进(例9)。

# Example 9: Good
article_list = Article.objects.filter(title__contains="django").values('title')
if article_list:print(article.title)article_list = Article.objects.filter(title__contains="django").values_list('id', 'title')
if article_list:print(article.title)

更新数据库部分字段请用update方法

如果需要对数据库中的某条已有数据或某些字段进行更新,更好的方式是用update,而不是save方法。我们现在可以对比下面两个案例。例10中需要把整个Article对象的数据(标题,正文.....)先提取出来,缓存到内存中,变更信息后再写入数据库。而例11直接对标题做了更新,不需要把整个文章对象的数据载入内存,显然更高效。尽管单篇文章占用内存不多,但是万一用户非常多呢,那么占用的内存加起来也是很恐怖的。

# Example 10: Bad
article = Article.objects.get(id=10)
Article.title = "Django"
article.save()# Example 11: Good
Article.objects.filter(id=10).update(title='Django')

only与defer

only会把括号内字段对应的值,封装到查询返回的对象中,通过对象点括号字段,不需要再走数据库查询,直接返回结果,一旦你点了不是括号内的字段 就会频繁的去走数据库查询# 惰性查询用不到的数据即使写了orm语句也不会执行res = models.Book.objects.values('title')  # 列表套字典res1 = models.Book.objects.only('title')  # 列表套对象# print(res1)for i in res1:# print(i.title)print(i.price)"""only括号内写什么字段生成的对象就含有对应的属性 在查找该属性的时候不再走数据库查询但是一旦查找括号内没有的字段属性 则每次都会走数据库查询"""# res1 = models.Book.objects.defer('title')# # print(res1)  # 列表套对象# for i in res1:#     # print(i.title)#     print(i.title)"""defer与only刚好相反生成的对象就不含有对应的属性 在查找该属性的时候需要每次走数据库查询但是一旦查找括号内没有的字段属性 则不需要走数据库查询总结:和 only相反,defer会将括号内的字段排除之外将其他字段对应的值, 直接封装到返回给你的对象中, 点其他字段 不需要再走数据库查询,一旦你点了括号内的字段就会有多少值,就会查询几次

select_related与prefech_related

Django ORM 中的 select_related() 和 prefetch_related() 都是用来优化查询效率,减少数据库查询次数的工具,它们的主要区别在于处理关联数据的方式和适用场景。下面通过具体例子来阐述两者的不同:

1. select_related()

select_related() 主要针对一对一(OneToOneField)和一对多(ForeignKey)关系。当查询一个模型对象时,如果知道后续会访问其关联对象,可以使用 select_related() 将关联对象的查询合并到原始查询中,通过 SQL 内联(INNER JOIN)操作一次性获取所有所需数据。这样可以避免对每个主对象单独发起查询来获取关联对象,显著减少数据库查询次数。

示例: 假设有一个 BlogPost 模型,它有一个外键 author 指向 Author 模型:

class Author(models.Model):name = models.CharField(max_length=100)class BlogPost(models.Model):title = models.CharField(max_length=200)author = models.ForeignKey(Author, on_delete=models.CASCADE)

如果我们想获取所有博客文章及其作者信息,不使用 select_related() 的情况下可能会这样查询:

posts = BlogPost.objects.all()for post in posts:print(post.title, post.author.name)

这样每次循环访问 post.author.name 时,都会触发一次数据库查询来获取作者信息。为了避免这种 N+1 查询问题,可以使用 select_related()

posts = BlogPost.objects.select_related('author')for post in posts:print(post.title, post.author.name)

此时,Django 会构造一条 SQL 查询,通过 INNER JOIN 把 Author 表的信息与 BlogPost 表一起查询出来,一次查询即可得到所有博客文章及其作者的完整数据。

2. prefetch_related()

prefetch_related() 主要用于处理多对多(ManyToManyField)关系以及其他类型的多对一(例如,一对一反向引用或一对多的多方向)关系。由于多对多关系可能导致 JOIN 后的数据集过于庞大,prefetch_related() 不使用 SQL JOIN,而是执行额外的查询来获取关联对象,然后在 Django 内存中完成对象之间的关联。这意味着它会在数据库层面分别查询主对象和关联对象,然后在 Python 环境中进行“连接”,通常使用的是 Django 的 Prefetch 对象来更精细地控制关联数据的加载。

示例: 假设 BlogPost 模型有一个多对多字段 tags,关联到 Tag 模型:

class Tag(models.Model):name = models.CharField(max_length=50)class BlogPost(models.Model):title = models.CharField(max_length=200)tags = models.ManyToManyField(Tag)

如果我们想获取所有博客文章及其标签列表,不使用 prefetch_related() 的情况下可能会这样查询:

posts = BlogPost.objects.all()for post in posts:for tag in post.tags.all():print(post.title, tag.name)

这样每次循环访问 post.tags.all() 时,都会触发一次数据库查询来获取文章的所有标签。为了避免这种 N+1 查询问题,可以使用 prefetch_related()

posts = BlogPost.objects.prefetch_related('tags')for post in posts:for tag in post.tags.all():print(post.title, tag.name)

posts = BlogPost.objects.prefetch_related('tags') 这一行代码表示对 BlogPost 模型的所有对象进行查询,并预先加载它们与 tags 多对多字段关联的所有 Tag 对象。Django 在执行这个查询时会遵循以下步骤:

  1. 查询所有博客文章: Django 首先执行一条 SQL 查询,从数据库中检索所有 BlogPost 的记录。这一步与没有使用 prefetch_related() 时获取博客文章列表是一样的。

  2. 执行另一个查询获取所有相关的标签: 在获取完所有博客文章之后,Django 会执行另一条 SQL 查询,用于检索与这些博客文章关联的所有 Tag 对象。这条查询通常会包含 IN 子句,其中包含先前查询出的所有博客文章的主键(通常是 id 字段)。例如,查询可能类似如下:

    SELECT * FROM tag WHERE id IN (1, 2, 3, ...);

    这个查询会返回所有与指定博客文章关联的标签数据。

  3. 在内存中将标签与对应的博客文章关联起来: 获取到所有博客文章和相关标签的数据库记录后,Django 不会在数据库层面进行任何进一步的 JOIN 操作。相反,它会在 Python 环境中(即内存中)根据数据库返回的数据构建关联关系。具体来说,Django 会将每个 Tag 对象与正确的 BlogPost 对象关联起来,这样当你遍历 post.tags.all() 时,尽管看起来像是在访问数据库,但实际上 Django 已经在内存中为你准备好了所有关联的标签数据,无需再向数据库发送查询请求。

    因此,在接下来的循环中:

    for post in posts:for tag in post.tags.all():print(post.title, tag.name)

    即使遍历每个 post 的所有 tags,也不会触发额外的数据库查询。这是因为 post.tags.all() 返回的是一个已预加载的查询集,其数据已经在第一步和第二步中从数据库加载到内存,并在第三步中完成了与对应博客文章的关联。

参考文章:[Django框架之ORM操作:多表查询,聚合查询、分组查询、F查询、Q查询、choices参数] - 刘较瘦丫 - 博客园 (cnblogs.com)

Django基础(13): QuerySet特性及高级使用技巧,如何减少数据库的访问,节省内存,提升网站性能_django queryset怎么不缓存-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/830508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka 3.x.x 入门到精通(08)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;08&#xff09;——对标尚硅谷Kafka教程 5. Kafka优化5.1 资源配置5.1.1 操作系统5.1.2 磁盘选择5.1.3 网络带宽5.1.4 内存配置5.1.5 CPU选择 5.2 集群容错5.2.1 副本分配策略5.2.2 故障转移方案5.2.3 数据备份与恢复 5.3 参数配置优化5.4 数…

【c++】mutable是一个关键字,用于指定一个类成员可以在一个const成员函数中被修改。

mutable是一个关键字,用于指定一个类成员可以在一个const成员函数中被修改。通常,当一个成员函数被声明为const时,这意味着这个函数不能修改它所属的对象。然而,有时候你可能需要在一个const成员函数中修改某个成员变量。这时,你就可以使用mutable关键字。webrtc的StunReq…

机器学习:深入解析SVM的核心概念(问题与解答篇)【三、核函数】

核函数 **问题一&#xff1a;为什么说是有限维就一定存在高维空间可分呢&#xff1f;**原始空间与特征空间为什么映射到高维空间可以实现可分核函数的作用 **问题二&#xff1a;最终怎么得到函数**从对偶问题到决策函数的步骤&#xff1a;结论 **问题三&#xff1a;为什么说特征…

在国内 PMP 有多少含金量?

PMP认证并不是对所有人都有价值&#xff0c;也并不是考到它必须会升值加薪&#xff0c;那可能就有人会问了&#xff0c;那我为什么还要考PMP&#xff1f;此言差矣&#xff0c;我个人项目管理行业混迹了这么多年了&#xff0c;真正对我有用的证书除了学历以外就是PMP认证了&…

Ubuntu 24.04 LTS (Noble Numbat) 正式版发布

Ubuntu 24.04 LTS (Noble Numbat) 正式版发布 Canonical 的第 10 个长期支持版本在性能工程、企业安全和开发人员体验方面树立了新标准 请访问原文链接&#xff1a;Ubuntu 24.04 LTS (Noble Numbat) 正式版发布&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。…

搭建基础镜像(centos+jdk)

搭建基础镜像&#xff08;centosjdk&#xff09; 1. 目录结构1.1 应用目录2.2 镜像目录 2. 编写Dockerfile2.1 设置工作目录2.2 解决时间同步问题&#xff08;设置时区&#xff09;2.3 核心逻辑2.4 设置环境变量 3. 构建镜像3.1 构建镜像3.2 导出镜像 1. 目录结构 1.1 应用目录…

10.MMD 室内场景导入背景视频和灯光

导入背景视频 1. 导入人物和场景 场景是Akali’s room&#xff0c;可以在墙壁上添加视频 先添加主场景 2. 修改视频文件格式 在背景里选择导入背景视频文件 需要将mp4视频格式转化为AVI格式 方法一 先将视频导入格式工厂 点击配置 将视频编码改成DivX 再开始处理 …

DockerUI安装使用

DockerUI安装使用 主机环境 [roottest01 ~]# uname -a Linux test01 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux [roottest01 ~]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core)安装 [roottest01 ~]# doc…

【算法基础实验】图论-UnionFind连通性检测之quick-find

Union-Find连通性检测之quick-find 理论基础 在图论和计算机科学中&#xff0c;Union-Find 或并查集是一种用于处理一组元素分成的多个不相交集合&#xff08;即连通分量&#xff09;的情况&#xff0c;并能快速回答这组元素中任意两个元素是否在同一集合中的问题。Union-Fin…

分布式存储 Ceph 的演进经验

从 2004 年到今天&#xff0c;Ceph 的存储后端一直都在演变&#xff0c;从最开始基于 B 树的 EBOFS 演变到今天的 BlueStore&#xff0c;存储后端已经变得非常成熟&#xff0c;新的存储系统不仅能够提供良好的性能&#xff0c;还有着优异的兼容性。我们在这篇文章中将要简单介绍…

Android SQLiteDatabase的使用详解

1、数据库–公共变量&#xff1a; 2、数据库–打开&#xff1a; 3、数据库–增&#xff1a; 4、数据库–删&#xff1a; 5、数据库–改&#xff1a; 6、数据库–查&#xff1a; 7、数据库–关闭&#xff1a; 8、数据库–辅助工具&#xff1a; 9、数据库–效果&…

配置DHCP和DNS

DHCP DHCP原理 作用&#xff1a;是一种网络协议&#xff0c;用于自动分配IP地址、子网掩码、默认网关、DNS服务器等TCP/IP参数 1.DHCP的四个报文 1.discover报文&#xff1a; 找寻dhcp服务器 2.offer报文&#xff1a; 服务器回复discover报文并且携带网络配置信息&#xff…

我在公司干了两年,有个在公司工作三年的成员要离职,接手别人代码才发现真的是一言难尽

微服务框架是别的团队的人搭建的&#xff0c;他负责单独开发一个报表模块&#xff0c;这是初始版本&#xff0c;还未上线 1、nacos做注册中心&#xff0c;却胡乱注册&#xff0c;服务命名有下划线 测试环境nacos配置命名空间为dev&#xff0c;直接与其他的微服务test命名空间…

Qt QLineEdit详解

1.简介 QLineEdit是一个单行文本编辑器。 行编辑允许用户使用一组有用的编辑功能输入和编辑单行纯文本&#xff0c;包括撤消和重做、剪切和粘贴以及拖放。 通过更改行编辑的echoMode&#xff0c;它也可以用作“只写”字段&#xff0c;用于密码等输入。 文本的长度可以限制为ma…

毅四捕Go设计模式笔记——命令模式

命令模式&#xff08;Command Pattern&#xff09; 为了解决什么问题&#xff1f; 命令模式的目的是将请求发起者和请求执行者解耦&#xff0c;使得请求的发起者不需要知道具体的执行者是谁&#xff0c;也不需要知道执行的具体过程&#xff0c;只需要发送请求即可。 通过使用…

如何轻松在D盘新建文件夹?意外丢失的文件夹怎么找回

对于很多刚接触电脑的朋友来说&#xff0c;如何正确地新建文件夹并将其放置在特定盘符&#xff08;如D盘&#xff09;可能是一个不小的挑战。同时&#xff0c;如果新建的文件夹突然消失&#xff0c;而我们又确信自己没有删除它&#xff0c;那么该如何找回呢&#xff1f;本文将为…

直播间怎么提高流量?巨量千川官方真实投流助力获客轻松翻倍

随着互联网的快速发展&#xff0c;直播已经成为了一种极具吸引力和互动性的娱乐和营销方式。然而&#xff0c;如何提高直播间的流量&#xff0c;(直播间流量&#xff1a;kxs7667)吸引更多观众成为了每个直播主都关注的重要问题。与此同时&#xff0c;巨量千川官方真实投流作为一…

如何在WordPress中设置网站的SEO标题和描述

在WordPress中&#xff0c;想要让你的网站在搜索引擎结果中脱颖而出&#xff0c;设置优秀的SEO标题和描述至关重要。这不仅可以帮助搜索引擎更好地理解你的网站内容&#xff0c;还可以吸引更多的点击率和流量。而选择一款合适的SEO插件是实现这一目标的关键之一。让我们来看看两…

pgvector扩展在IvorySQL Oracle兼容模式下的应用实践

向量数据库是生成式人工智能(GenAI)的关键组成部分。作为PostgreSQL的重要扩展&#xff0c;pgvector支持高达16000维的向量计算能力&#xff0c;使得PostgreSQL能够直接转化为高效的向量数据库。 IvorySQL基于PostgreSQL开发&#xff0c;因此它同样支持添加pgvector扩展。在Ora…

部署YUM仓库及NFS共享服务

YUM yum仓库常用类型&#xff1a; 本地源仓库&#xff1a;baserulfile:// 在线源仓库&#xff1a;baserulhttp:// ftp源仓库&#xff1a;baseru&#xff1a;ftp&#xff1a;// 实验&#xff1a; 首先安装软件 [rootlocalhost yum.repos.d]# cd /mnt/Packages/ [rootloca…