使用Django / Django REST Framework项目中的预取层提高性能和可维护性
项目描述
Django虚拟模型
使用Django / Django REST Framework项目中的预取层提高性能和可维护性
文档: 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_related
或select_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
时,将出现以下异常
如果您不使用DRF序列化器,请手动使用虚拟字段填充您的查询集
qs = VirtualMovie().get_optimized_queryset(
Movie.objects.all(),
lookup_list=[
"directors__awards",
"directors__nomination_count",
]
)
项目详情
下载文件
下载适合您平台的项目文件。如果您不确定要选择哪个,请了解更多关于安装软件包的信息。