jbo*_*ily 7 python django django-serializer django-rest-framework
有几十篇关于 Django 嵌套关系中的 n+1 查询的帖子,但我似乎无法找到我的问题的答案。这是上下文:
模型
class Book(models.Model):
title = models.CharField(max_length=255)
class Tag(models.Model):
book = models.ForeignKey('app.Book', on_delete=models.CASCADE, related_name='tags')
category = models.ForeignKey('app.TagCategory', on_delete=models.PROTECT)
page = models.PositiveIntegerField()
class TagCategory(models.Model):
title = models.CharField(max_length=255)
key = models.CharField(max_length=255)
Run Code Online (Sandbox Code Playgroud)
一本书有很多标签,每个标签属于一个标签类别。
序列化器
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
exclude = ['id', 'book']
class BookSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, required=False)
class Meta:
model = Book
fields = ['title', 'tags']
def create(self, validated_data):
with transaction.atomic():
tags = validated_data.pop('tags')
book = Book.objects.create(**validated_data)
Tag.objects.bulk_create([Tag(book=book, **tag) for tag in tags])
return book
Run Code Online (Sandbox Code Playgroud)
问题
我正在尝试BookViewSet
使用以下示例数据POST :
{
"title": "The Jungle Book"
"tags": [
{ "page": 1, "category": 36 }, // plot intro
{ "page": 2, "category": 37 }, // character intro
{ "page": 4, "category": 37 }, // character intro
// ... up to 1000 tags
]
}
Run Code Online (Sandbox Code Playgroud)
这一切都有效,但是,在发布期间,序列化程序继续调用每个标签以检查它是否category_id
有效:
一次调用中多达 1000 个嵌套标签,我负担不起。
我如何“预取”进行验证?
如果这是不可能的,我该如何关闭检查外键 ID 是否在数据库中的验证?
编辑:附加信息
这是视图:
class BookViewSet(views.APIView):
queryset = Book.objects.all().select_related('tags', 'tags__category')
permission_classes = [IsAdminUser]
def post(self, request, format=None):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Run Code Online (Sandbox Code Playgroud)
DRF 序列化器不是优化数据库查询的地方(在我看来)。序列化器有 2 个工作:
\n\n因此,优化查询的正确位置是相应的视图。
\n我们将使用select_related
以下方法:
\n\n\n返回一个 QuerySet,它将 \xe2\x80\x9cfollow\xe2\x80\x9d 外键关系,在执行查询时选择其他相关对象数据。这是一个性能增强器,会导致单个更复杂的查询,但意味着稍后使用外键关系将\xe2\x80\x99 不需要数据库查询。\n 以避免 N+1 数据库查询。
\n
您将需要修改视图代码中创建相应查询集的部分,以便包含调用select_related
。
\n您还需要添加related_name
一个Tag.category
字段定义中。
例子:
\n\n# In your Tag model:\ncategory = models.ForeignKey(\n \'app.TagCategory\', on_delete=models.PROTECT, related_name=\'categories\'\n)\n\n# In your queryset defining part of your View:\nclass BookViewSet(views.APIView):\n\n queryset = Book.objects.all().select_related(\n \'tags\', \'tags__categories\'\n ) # We are using the related_name of the ForeignKey relationships.\n
Run Code Online (Sandbox Code Playgroud)\n\n如果您想测试一些不同的东西,也使用序列化器来减少查询数量,您可以查看这篇文章。
\n 归档时间: |
|
查看次数: |
2562 次 |
最近记录: |