orm优化数据库访问:https://docs.djangoproject.com/en/1.11/topics/db/optimization/
一、QuerySet
可迭代
querysey=models.Book.objects.all() for book in querysey: print(book.title)
可切片
Book.objects.all()[:3] Book.objects.all()[3:6] # 不支持负的索引,例如Book.objects.all()[0:-1]。通常,查询集的切片返回一个新的查询集,它不会执行查询。
惰性查询
QuerySet 是懒惰的 -- 创建查询集不会带来任何数据库的访问;直到查询集需要求值时,Django 才会真正运行这个查询。
queryset = models.Book.objects.all() # not hits database print(queryset) # hits database for book in queryset: print(book.title) # Hits database
缓存机制
每个查询集都包含一个缓存来最小化对数据库的访问。理解它是如何工作的将让你编写最高效的代码。
在一个新创建的查询集中,缓存为空。查询集的求值将重用缓存的结果。
请牢记这个缓存行为,因为对查询集使用不当的话,它会坑你的。查询集,对它们求值,然后扔掉它们:
print(book.title for book in models.Book.objects.all()) # hits database print(book.price for book in models.Book.objects.all()) # hits database
这意味着相同的数据库查询将执行两次,显然倍增了你的数据库负载。同时,还有可能两个结果列表并不包含相同的数据库记录,因为在两次请求期间有可能有 book 查询集并重新使用它:
queryset = models.Book.objects.all() print(book.title for book in queryset) print(book.price for book in queryset) # 下面也是一次数据库查询 for book in querysey: print(book.title) print(book.price)
何时查询集不会被缓存?
,这意味着使用切片或索引来限制查询集将不会填充缓存。
例如,重复获取查询集对象中一个特定的索引将每次都查询数据库:
queryset = models.Book.objects.all() print(queryset[1]) # hits database print(queryset[1]) # hits database
然而,如果已经对全部查询集求值过,则将检查缓存:
queryset = models.Book.objects.all() [book for book in queryset] # hits database print(queryset[1]) # use cache print(queryset[1]) # use cache
简单地打印查询集不会填充缓存:
queryset = models.Book.objects.all() print(queryset) # hits database print(queryset) # hits database
exists() 和 iterator()
简单的使用 if 语句进行判断也会完全执行整个 queryset 并且把数据放入cache,虽然你并不需要这些数据!为了避免这个,可以用 exists() 方法来检查是否有数据:
queryset = models.Author.objects.all() if queryset.exists(): print("True")
更优于:
queryset = models.Author.objects.all() if queryset: print("True")
它们的sql查询分别为:
SELECT (1) AS "a" FROM "app01_author" LIMIT 1; args=() SELECT "app01_author"."id", "app01_author"."name", "app01_author"."age", "app01_author"."author_detail_id" FROM "app01_author"; args=()
当queryset非常巨大时,cache会成为问题。
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让程序濒临崩溃。要避免在遍历数据的同时产生 queryset cache,可以使用 iterator() 方法来获取数据,处理完数据就将其丢弃。
# iterator() 可以一次只从数据库获取少量数据,这样可以节省内存 objs = models.Book.objects.all().iterator() for obj in objs: print(obj.title) # 注意,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 for obj in objs: print(obj.title)
当然,使用 iterator() 方法来防止生成cache,意味着遍历同一个 queryset 时会重复执行查询。所以使用 iterator() 的时候要当心,确保你的代码在操作一个大的 queryset 时没有重复执行查询。
总结
queryset 的 cache 是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用 exists() 和 iterator() 方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。
二、查询优化
from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用户信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(verbose_name="联系电话", max_length=11, null=True, unique=True) avatar = models.FileField(verbose_name="头像", upload_to="avatars/", default="/avatars/default.png") create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) blog = models.OneToOneField(verbose_name="个人站点", to="Blog", to_field="nid", null=True) def __str__(self): return self.username class Blog(models.Model): """ 博客信息 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name="个人博客标题", max_length=64) site = models.CharField(verbose_name="个人博客后缀", max_length=32, unique=True) theme = models.CharField(verbose_name="博客主题", max_length=32) def __str__(self): return self.title class Category(models.Model): """ 个人文章分类表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name="分类标题", max_length=32) blog = models.ForeignKey(verbose_name="所属博客", to="Blog", to_field="nid") # 一个博客站点可以有多个分类 def __str__(self): return self.title class Tag(models.Model): """ 标签表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name="标签名称", max_length=32) blog = models.ForeignKey(verbose_name="所属博客", to="Blog", to_field="nid") # 一个博客站点可以有多个标签 def __str__(self): return self.title class Article(models.Model): """ 文章表 """ nid = models.AutoField(primary_key=True) title = models.CharField(max_length=50, verbose_name="文章标题") description = models.CharField(max_length=255, verbose_name="文章描述") create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) comment_count = models.IntegerField(verbose_name="评论数", default=0) up_count = models.IntegerField(verbose_name="点赞数", default=0) down_count = models.IntegerField(verbose_name="反对数", default=0) category = models.ForeignKey(verbose_name="所属分类", to="Category", to_field="nid", null=True) user = models.ForeignKey(verbose_name="作者", to="UserInfo", to_field="nid") tags = models.ManyToManyField( verbose_name="所属标签", to="Tag", through="ArticleToTag", through_fields=("article", "tag"), ) def __str__(self): return self.title class ArticleDetail(models.Model): """ 文章详细表 """ nid = models.AutoField(primary_key=True) content = models.TextField(verbose_name="文章内容") article = models.OneToOneField(verbose_name="关联文章", to="Article", to_field="nid") class ArticleToTag(models.Model): """ 文章和标签的多对多关系表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name="文章", to="Article", to_field="nid") tag = models.ForeignKey(verbose_name="标签", to="Tag", to_field="nid") class Meta: unique_together = [ ("article", "tag"), ] def __str__(self): v = self.article.title + "--" + self.tag.title return v class ArticleUpDown(models.Model): """ 点赞表 哪个用户对哪篇文章点了赞 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey(verbose_name="用户id", to="UserInfo", null=True) article = models.ForeignKey(verbose_name="文章id", to="Article", null=True) is_up = models.BooleanField(verbose_name="是否为赞", default=True) class Meta: unique_together = [ ("article", "user"), ] class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey(verbose_name="评论者", to="UserInfo", to_field="nid") article = models.ForeignKey(verbose_name="评论文章", to="Article", to_field="nid") create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) content = models.CharField(verbose_name="评论内容", max_length=255) parent_comment = models.ForeignKey("self", null=True) def __str__(self): return self.content