python – Django Serializer嵌套创建:如何避免N 1查询关系

在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是否是有效的:

《python – Django Serializer嵌套创建:如何避免N 1查询关系》

在通话中最多有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.

点赞