跳转到主要内容

根据需要自动预取外键值。

项目描述

https://img.shields.io/github/actions/workflow/status/tolomea/django-auto-prefetch/main.yml.svg?branch=main&style=for-the-badge https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge https://img.shields.io/pypi/v/django-auto-prefetch.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

根据需要自动预取外键值。

目的

当访问模型实例上的ForeignKeyOneToOneField(包括反向)时,如果该字段的值尚未加载,则自动预取将为当前模型实例通过同一QuerySet加载的所有模型实例预取该字段。这在模型级别上启用,完全自动且对模型使用者完全透明。

要求

支持Python 3.8到3.12。

支持Django 3.2到5.1。

用法

  1. 使用 python -m pip install django-auto-prefetch 安装。

  2. 将这些导入从 django.db.models 更改为 auto_prefetch

    • 外键

    • 管理员

    • 模型 - 包括从 auto_prefetch.Model.Meta 继承 Meta

    • 一对一字段

    • 查询集

    如果你使用这些类的任何自定义子类,你应该能够在子类的基类中交换为 auto_prefetch 版本。

    例如,如果你有

    from django.db import models
    
    
    class Book(models.Model):
        author = models.ForeignKey("Author", on_delete=models.CASCADE)
    
        class Meta:
            verbose_name = "Book"

    …交换为

    import auto_prefetch
    from django.db import models
    
    
    class Book(auto_prefetch.Model):
        author = auto_prefetch.ForeignKey("Author", on_delete=models.CASCADE)
    
        class Meta(auto_prefetch.Model.Meta):
            verbose_name = "Book"
  3. 运行 python manage.py makemigrations 以生成你修改的所有模型的迁移。这些迁移将每个转换的模型的 Meta.base_manager_name 选项 设置为 prefetch_manager。此更改确保在相关管理器上发生自动预取。此类迁移不会更改数据库中的任何内容。

    (如果你在模型上设置 Meta.base_manager_name,请确保它继承自 auto_prefetch.Manager。))

背景和原因

目前,当访问未缓存的键字段时,Django 会自动从数据库中获取缺失的值。当这种情况在循环中发生时,它会创建 1+N 查询问题。考虑以下代码片段

for choice in Choice.objects.all():
    print(choice.question.question_text, ":", choice.choice_text)

这将对选项执行一次查询,然后对每个选项执行一次查询以获取该选项的问题。

可以通过正确应用 prefetch_related() 来避免这种行为,如下所示

for choice in Choice.objects.prefetch_related("question"):
    print(choice.question.question_text, ":", choice.choice_text)

这有几个可用性问题,特别是

  • 经验较少的用户通常不知道这是必要的。

  • 模板等外观上的更改可能会改变应预取的字段。

  • 与此相关,需要 prefetch_related()(例如,模板)的代码可能离需要应用 prefetch_related() 的地方(例如,视图)很远。

  • 找到缺失的 prefetch_related() / select_related() 调用并不简单,需要持续进行。

  • prefetch_related() 调用中的多余条目甚至更难找到,并导致不必要的数据库查询。

  • 对于像 admin 和 Django Rest Framework 这样的库来说,自动生成正确的 prefetch_related() 子句非常困难。

在上面的示例中,当我们在循环的第一迭代中首次访问选项的问题字段时,而不是只为那个选项获取问题,自动预取将投机性地获取 QuerySet 返回的所有选项的问题。此更改导致第一个代码片段具有与第二个代码片段相同的数据库行为,同时减少或消除了所有上述可用性问题。

一些重要点

  • ManyToManyField 实际上没有改变。

  • 因为这些是 ForeignKeyOneToOneField,生成的查询不能有比原始查询更多的结果行,可能更少。这消除了关于乘法查询大小爆炸的任何担忧。

  • 此功能永远不会导致更多数据库查询,因为预取只会发布 ORM 已经打算获取单个相关对象的那些地方。

  • 因为它由获取缺失的相关对象触发,所以它不会更改完全由 prefetch_related() 和/或 select_related() 调用覆盖的代码的数据库行为。

  • 它将自动跨关系链,如 choice.question.author。在上述条件下,这些条件仍然适用。

  • 在少数罕见情况下,它可能导致数据库和 Django 之间更大的数据传输(见下文)。

这个最后一点的例子是

qs = Choice.objects.all()
list(qs)[0].question

这样的例子通常比较少见,在代码审查期间更容易被发现(与模板中的 {{ choice.question }} 相比)。较大的查询通常比生成数百个查询更好的失败模式。要使实际上产生较差的行为,你需要:* 获取大量选项 * 筛选出几乎所有的选项 * … 以防止未筛选项的垃圾收集

如果其中任何一个不成立,则自动预取仍然会产生与不预取相当或更好的数据库行为。

另请参阅

附言。

如果你有任何疑虑,请查看代码,它都在 auto_prefetch/__init__.py 中,相当短。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分布

django_auto_prefetch-1.9.0.tar.gz (8.2 kB 查看哈希值)

上传时间:

构建分布

django_auto_prefetch-1.9.0-py3-none-any.whl (7.0 kB 查看哈希值)

上传时间: Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面