在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)
一本书有很多标签,每个标签都属于一个标签类别.
序列化器
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
问题
我正在尝试使用以下示例数据POST到BookViewSet:
{
"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
]
}
这一切都有效,但是,在帖子中,序列化程序继续调用每个标记来检查category_id是否是有效的:
在通话中最多有1000个嵌套标签,我买不起.
如何“预取”验证?
如果这是不可能的,如何关闭检查foreign_key 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)
最佳答案 DRF序列化程序不是(我自己认为)优化数据库查询的地方. Serializer有2个职位:
>序列化并检查输入数据的有效性.
>序列化输出数据.
因此,优化查询的正确位置是相应的视图.
我们将使用select_related
方法:
Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries.
to avoid the N+1 database queries.
您需要修改创建相应查询集的视图代码部分,以便包含select_related调用.
您还需要在Tag.category字段定义中添加related_name.
例:
# In your Tag model:
category = models.ForeignKey(
'app.TagCategory', on_delete=models.PROTECT, related_name='categories'
)
# In your queryset defining part of your View:
class BookViewSet(views.APIView):
queryset = Book.objects.all().select_related(
'tags', 'tags__categories'
) # We are using the related_name of the ForeignKey relationships.
如果你想测试一些不同的东西,它们也使用序列化程序来减少查询次数,你可以查看this article.