Django - 当提供 .values() 时,StringRelatedField() 不适用于查询结果

Django - StringRelatedField() not applied to query results when .values() provided

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

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

如果我的查询 return 包含所有字段,这非常有效...所以查询

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

成功了。

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

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

但该查询的结果具有 User table 记录的 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)

任何指导将不胜感激。

这是 StringRelatedField()

的预期行为

StringRelatedField returns str 值的表示 和你的情况,用户有一个 integer 值。如果您使用 QuerySet(不调用 values()),用户将有一个 User 实例,因此 str 表示将为您提供正确的结果。

您感到性能有所提高,因为 values('id', 'location', 'user', ) 从单个 table 中获取值(请注意,调用 user 不会在此处进行内部联接)并且有没有机会出现 N+1 问题。但是,如果您使用用户的 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
        )<b>.select_related("user")</b>