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
表数据

相关文章:

  • 2022-01-27
  • 2022-12-23
  • 2022-12-23
  • 2021-09-19
  • 2022-02-09
  • 2022-12-23
  • 2021-04-21
猜你喜欢
  • 2021-09-10
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-20
  • 2021-10-16
相关资源
相似解决方案