跳转到主要内容

使用Django / Django REST Framework项目中的预取层提高性能和可维护性

项目描述

Django Virtual Models Icon

Django虚拟模型

使用Django / Django REST Framework项目中的预取层提高性能和可维护性

Test   Coverage Status   Package version   Supported Python versions


文档: https://vintasoftware.github.io/django-virtual-models/

示例项目https://github.com/vintasoftware/django-virtual-models/tree/main/example

源代码https://github.com/vintasoftware/django-virtual-models


Django Virtual Models为Django代码库引入了一个新的“预取层”,帮助开发者在不牺牲可维护性、可组合性和性能的情况下表达复杂的读取逻辑。虚拟模型允许开发者在一个声明性类中声明所有需要的嵌套、注释、预取和连接。

在实现Django REST Framework序列化器时,开发者需要注意避免由于关联查询集上缺少prefetch_relatedselect_related调用而导致的N+1选择问题。此外,开发者必须不遗漏对在查询集级别计算的字段的annotate调用。

通过与DRF的虚拟模型集成,如果您更改DRF序列化器,您就不会忘记修改关联的查询集,添加额外的注释、预取和连接。如果您忘记更新查询集,Django Virtual Models将通过引发友好的异常来引导您编写正确的虚拟模型,以帮助您更改的序列化器。这种指导将防止所有使用虚拟模型的序列化器中的N+1和缺少注释。

例如,假设您从MovieSerializer开始有以下嵌套序列化器

from movies.models import Nomination, Person, Movie

class AwardSerializer(serializers.ModelSerializer):
    class Meta:
        model = Nomination
        fields = ["award", "category", "year", "is_winner"]

class PersonSerializer(serializers.ModelSerializer):
    awards = AwardSerializer(many=True)
    nomination_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Person
        fields = ["name", "awards", "nomination_count"]

class MovieSerializer(serializers.ModelSerializer):
    directors = PersonSerializer(many=True)

    class Meta:
        model = Movie
        fields = ["name", "directors"]

为了良好的性能和正确的功能,所有嵌套序列化器都必须在MovieSerializer使用的查询集上有一个对应的prefetch_related。此外,应该在nomination_count字段上annotate它。因此,您需要编写这个复杂的嵌套预取链

from django.db.models import Prefetch

awards_qs = Nomination.objects.filter(is_winner=True)

directors_qs = Person.objects.prefetch_related(
    Prefetch(
        "nominations",
        queryset=awards_qs,
        to_attr="awards"
    )
).annotate(
    nomination_count=Count("nominations")
).distinct()

qs = Movie.objects.prefetch_related(
    Prefetch(
        "directors",
        queryset=directors_qs
    )
)

相反,您可以声明用于此读取逻辑的虚拟模型,以便在代码库的多个地方轻松重用和自定义这些类

import django_virtual_models as v

class VirtualAward(v.VirtualModel):
    class Meta:
        model = Nomination

    def get_prefetch_queryset(self, **kwargs):
        return Nomination.objects.filter(is_winner=True)


class VirtualPerson(v.VirtualModel):
    awards = VirtualAward(lookup="nominations")
    nomination_count = v.Annotation(
        lambda qs, **kwargs: qs.annotate(
            nomination_count=Count("nominations")
        ).distinct()
    )

    class Meta:
        model = Person


class VirtualMovie(v.VirtualModel):
    directors = VirtualPerson()

    class Meta:
        model = Movie

要配置您的DRF视图和序列化器以使用虚拟模型,请从适当的类继承

import django_virtual_models as v

class MovieSerializer(v.VirtualModelSerializer):
    ...

    class Meta:
        ...
        virtual_model = VirtualMovie

class MovieList(v.VirtualModelListAPIView):
    queryset = Movie.objects.all()
    serializer_class = MovieSerializer
    ...

然后库将自动为您执行正确的预取和注释!

例如,如果您忘记在VirtualPerson上添加nomination_count字段,当使用MovieSerializer时,将出现以下异常

MissingVirtualModelFieldException exception

如果您不使用DRF序列化器,请手动使用虚拟字段填充您的查询集

qs = VirtualMovie().get_optimized_queryset(
    Movie.objects.all(),
    lookup_list=[
        "directors__awards",
        "directors__nomination_count",
    ]
)

要了解更多信息,请查看安装教程。或者示例项目

项目详情


下载文件

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

源代码发行版

django-virtual-models-0.2.0.tar.gz (53.3 kB 查看哈希值)

上传时间 源代码

构建发行版

django_virtual_models-0.2.0-py3-none-any.whl (21.4 kB 查看哈希值)

上传时间 Python 3

支持者