【问题标题】:django-rest-framework override get_queryset and pass additional data to serializer along querysetdjango-rest-framework 覆盖 get_queryset 并将附加数据沿查询集传递给序列化程序
【发布时间】:2021-12-24 14:54:33
【问题描述】:

这是我面临的问题的一个玩具示例。出于某种原因,我无法将预期的数据传递给我的序列化程序,这会引发以下错误。

/my-end-point/处的AttributeError

尝试在序列化程序 ParentSerializer 上获取字段 main_data 的值时出现 AttributeError。 序列化程序字段可能命名不正确,并且与 str 实例上的任何属性或键都不匹配。 原始异常文本是:“str”对象没有属性“main_data”。

class MainModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MainModel


class ParentSerializer(serializers.Serializer):
    
    main_data = MainModelSerializer(many=True)
    extra_data = serializers.FloatField()
    

class View(ListCreateAPIView):
    
    serializer = ParentSerializer
    
    def get_queryset(self):
        # extra_data would have some calculated value which would be a float
        extra_data = some_calculated_value()
        queryset = MainModel.objects.filter(some filters)
        
        return {
            'main_data': queryset,
            'extra_data': extra_data
        }
        
        
# expected data that is passed to the ParentSerializer

# {
#     'main_data': queryset,
#     'extra_data': extra_data
# }

【问题讨论】:

  • get_queryset 只返回一个查询集。无法返回字典
  • 那么有没有办法向我的序列化器发送额外的数据?
  • 我这样做了,但分页类无法使用。

标签: django django-rest-framework django-views django-serializer


【解决方案1】:

看起来你有两个问题:

  1. get_queryset 方法的返回值不正确
  2. 向序列化程序传递一些额外数据

至于第一个,您根本不应该覆盖该方法或返回查询集。根据计算额外数据的方式,可能需要权衡取舍,例如查询集上的 annotating 值并在序列化程序 fields list 中指定它。

对于第二个 - 如果注释不是一个选项,并且您想计算 value one,可能的解决方案是向序列化程序 context 添加额外数据并将其用作 serializer method field 的返回值。在这种情况下,额外数据将与模型记录数据放在同一级别(每个模型记录的额外字段):

{
    'model_field_1': 'value',
    ...
    'extra_field': 'value',
}

或者您可以通过覆盖list(和create,如果需要)继续使用nested relationships 的方法 - 只需向序列化程序验证数据添加额外数据,结果将如下所示:

{
    'extra_data': 'value',
    'main_data': [
        {'id': 1, 'field1': 'value', ...}
        ...
    ]
}
class View(ListCreateAPIView):
    
    serializer = ParentSerializer
    
    def list(self, request, *args, **kwargs):
        # extra_data would have some calculated value which would be a float
        extra_data = some_calculated_value()
        qs = self.get_queryset()
        data = self.get_serializer(qs, many=True).data
        data['extra_data'] = extra_data
        return Response({'data': data}, status=status.HTTP_200_OK, content_type = 'application/json' ) 

【讨论】:

  • 嵌套解决方案的问题在于,序列化程序的方法字段会为表中的每一行调用,该字段在“n”行中命中数据库“n”次。我想要数据库中的'n'行和'extra_data'的一个值响应看起来像这样:{'main_data':[{...},{...},{...}, ... ], 'extra_data: (a float value) } 但是使用嵌套方法我得到这个: [ {...main_data, 'extra_data' : float}, {...main_data, 'extra_data' : float}, . ..]
  • 我的回答中的第二种方法将完全满足您的需要,对许多记录的嵌套 appoarch 引用将被序列化为字典列表,而不是添加到每个列表字典中的额外数据。此外,如果值通过上下文传递,它不会比在视图中计算时更多地击中 DB。最后,如果由于某些原因(可能不是因为您现在面临的问题),您需要避免额外的命中以在序列化程序中获得相同的值 - 您可以使用 LRU cache 装饰序列化程序方法字段
【解决方案2】:

我认为您想要实现的是将一些额外的数据传递给ParentSerializer 以由您查看的list action 返回。但是您的代码中有多个问题:

  1. get_queryset 仅用于返回models.QuerySet 的实例,用于确定视图允许对哪些模型对象执行任何类型的操作。
  2. 这是对第一点的跟进,get_queryset 不用于将数据传递给序列化程序,这是在 Serializer 初始化期间完成的。
  3. 您正在使用ModelSerializer,但您没有在Meta 类上指定fields 属性,因为drf version 3.3 不再允许,您必须定义fields 属性或@987654334 @ 属性。

至于实际上如何将额外的数据传递给序列化器,您可以通过多种方式做到这一点:

  1. 定义一个 SerializerMethodField 由Charnel 在他的回答中提出,由 drf 文档here 描述,您的序列化程序将如下所示:
    class ParentSerializer(serializers.Serializer):
        main_data = MainDataSerializer(many=True)
        extra_data = serializers.SerializerMethodField(method_name="some_calculated_value")

        def some_calculated_value(self, obj):
            # calculate value here
            return value

请注意,此字段是只读字段。

  1. 您可以在验证期间将计算值注入最终结果,这将使您的序列化程序如下所示:
    class ParentSerializer(serializers.Serializer):
        main_data = MainDataSerializer(many=True)

        def validate(self, attrs):
            attrs["extra_data"] = some_calculated_value()
            return attrs
  1. 您也可以通过覆盖视图上的list 函数并更改传递给序列化程序的数据来实现。

我个人更喜欢选项 1,因为 SerializerMethodField 符合目的,而 validate 函数应仅用于验证以遵守单一职责概念。

【讨论】:

    猜你喜欢
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 2014-05-24
    • 1970-01-01
    • 2015-09-25
    相关资源
    最近更新 更多