【问题标题】:Django DRF serializer method field on many to many running 2n queries多对多运行 2n 查询上的 Django DRF 序列化程序方法字段
【发布时间】:2020-12-18 17:01:56
【问题描述】:

我正在使用 Django 2.2Django REST Framework

我有以下模型结构

class MyModel(models.Model):
  name = models.ChartField(max_length=200)

class Tag(models.Model):
  name = models.ChartField(max_length=50, unique=True)

class MyModelRelation(models.Model):
  obj = models.ForeignKey(MyModel, related_name='relation')
  user = models.ForeignKey(User)
  tags = models.ManyToManyField(Tag)

  def tag_list(self):
     return self.tags.all().values_list('name', flat=True).distinct()

我想用MyModel 实例获取标签列表,为此,序列化程序是

class MyModelSerializer(serializers.ModelSerializer):
  tags_list = serializers.SerializerMethodField(read_only=True)

  def get_tags_list(self, obj):
     return obj.relation.tag_list()

  class Meta:
    fields = [
      'name',
      'tags_list'
    ]

视图是

class ObjListView(ListAPIView):
  serializer_class = MyModelSerializer
  
  def get_queryset(self):
    return super().get_queryset().select_related('relation').prefetch_related('relation__tags')

但要获得 58 条记录,它运行了将近 109 个查询。

my_app_mymodel`, `my_app_mymodelrelation_tags 重复多次

【问题讨论】:

  • 这是由于模型类中的tag_list 方法。每个模型实例都会调用它,而且很可能会调用两次。请注意,此类查询作为新的数据库查询运行,因此您的 select_related 或 prefetch_related 没有帮助。您可以尝试注释tag_list 而不是使用方法
  • 我在序列化方法字段中使用了相同的逻辑,并且查询的数量仍然相同。能举个带注释的例子吗?
  • 在下面查看我的答案

标签: django django-rest-framework many-to-many django-orm


【解决方案1】:

这就是我建议您解决问题的方法。您可以在序列化程序级别中提取名称,而不是在 DB 级别中提取名称。它将使事情变得更容易和更快。首先,从模型类中删除tag_list 方法。首先将注释添加到您的视图中: 从 django.db.models 导入 F

def get_queryset(self):
    return super().get_queryset().annotate(tags_list=F('relation__tags')).select_related('relation')

然后在您的序列化程序中

class MyModelSerializer(serializers.ModelSerializer):
  tags_list = serializers.SlugRelatedField(many=True, slug_field='name', read_only=True)
...

【讨论】:

  • TypeError: 'int' object is not iterable
  • 我没有运行这段代码,所以我不知道错误。您至少可以尝试找出类型错误的来源吗?堆栈跟踪应该向您显示
  • 我刚刚看到我在注释中省略了一个括号。我不知道这是否是导致类型错误的原因,但您可以再试一次。如果您给出错误,请尝试查看它的来源并让我知道,因为我自己无法运行代码
  • 括号不是问题。在Serializer库内部调试后,上面的字段被转换为ManyRelatedField(child_relation=SlugRelatedField(read_only=True, slug_field='name'), read_only=True),并且属性是一个整数,而不是可迭代的。 ManyRelatedField.to_representation() 需要一个可迭代的,但它正在获取一个整数。
猜你喜欢
  • 1970-01-01
  • 2018-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-11
  • 2014-08-05
  • 1970-01-01
相关资源
最近更新 更多