首页 > 解决方案 > Django - 提供 .values() 时,StringRelatedField() 不应用于查询结果

问题描述

在我的 Django 应用程序中,我发现StringRelatedField()当序列化程序用于已指定一组字段值的过滤器查询时,没有应用序列化程序类上的 a - 尽管如果查询返回所有字段它确实有效。唷,那是一口,让我分解一下-

我有两个模型,ReportUser;每个Report都与一个User. 理想情况下,我想通过get_queryset和在每个报告记录中查询报告,关联的值user应该是模型上方法StringRelatedField()的返回值(即用户的名字和姓氏)的结果。__str__User

如果我的查询返回所有字段,这完美地工作......所以查询

Report.objects.filter(location__within=some_other_model.region)

成功了。

但是,我发现只查询values我实际需要的可以极大地提高性能,所以我更喜欢这样查询:

Report.objects.filter(location__within=some_other_model.region).values('id',
                                                                       'location',
                                                                       'user',)

但是该查询的结果具有User表中记录的 UUID 外键,它们似乎没有被替换为StringRelatedField().

下面,我有 的视图和序列化程序Report,以及User模型。

# report/view.py

class ReportViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ReportReadSerializer

    def get_queryset(self):
            some_other_model = get_object_or_404(Organization, pk='some_other_model_id')

            # remove the .values() and this works correctly
            return Report.objects.filter(location__within=some_other_model.region).values('id',
                                                                                           'location',
                                                                                           'user',)
# report/serializers.py

class ReportReadSerializer(serializers.GeoFeatureModelSerializer):
    location = serializers.GeometryField(precision=4)
    user = StringRelatedField()

    class Meta:
        model = Observation
        geo_field = 'location'
        fields = [
            'id',
            'location',
            'user',
        ]
# user/models.py

class User(SoftDeletionModel):
    def __str__(self):
        return '%s %s' % (self.first_name, self.last_name)

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

任何指导将不胜感激。

标签: djangodjango-viewsdjango-serializer

解决方案


这是预期的行为StringRelatedField()

StringRelatedField返回值的表示str形式,在您的情况下,用户具有数值。如果您使用的是QuerySet(不调用values()),则用户将拥有一个User实例,因此str表示将为您提供正确的结果。

values('id', 'location', 'user', )由于从单个表中获取值(请注意,调用user不会在此处进行内部连接)并且没有出现问题的机会,您已经感受到了性能改进N+1。但是,如果您使用 User 的str方法,Django 将进行内部连接,并且会出现N+1问题,因此您将遇到性能问题。

所以,你有两个选择,

  1. 使用值并忽略User' 表示
  2. 利用select_related()
class ReportViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ReportReadSerializer

    def get_queryset(self):
        some_other_model = get_object_or_404(Organization, pk='some_other_model_id')
        return Report.objects.filter(
            location__within=some_other_model.region
        ).select_related("user")

推荐阅读