繁体   English   中英

django-rest-framework 覆盖 get_queryset 并沿查询集将附加数据传递给序列化程序

[英]django-rest-framework override get_queryset and pass additional data to serializer along queryset

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

/my-end-point/ 处的 AttributeError

尝试获取序列化ParentSerializer main_data上的字段main_data的值时出现ParentSerializer 序列化器字段可能命名不正确,并且与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
# }

看起来你有两个问题:

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

至于第一个,您根本不应该覆盖该方法或返回查询集。 根据计算额外数据的方式,可以进行权衡,例如在查询集上注释值并在序列化器字段列表中指定它。

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

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

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

{
    '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' ) 

我认为您想要实现的是将一些额外的数据传递给ParentSerializer ,以便您的视图上的list action返回。但是您的代码中存在多个问题:

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

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

  1. 定义一个由Charnel在他的回答中提出的 SerializerMethodField , 这里的 drf 文档描述,您的序列化程序将如下所示:
    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函数应该仅用于验证以遵守单一职责概念。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM