简体   繁体   English

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

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

This is a toy example of the problem that I am facing.这是我面临的问题的一个玩具示例。 For some reason, I am unable to pass the expected data to my serializer and that is raising the following error.出于某种原因,我无法将预期的数据传递给我的序列化程序,这引发了以下错误。

AttributeError at /my-end-point/ /my-end-point/ 处的 AttributeError

Got AttributeError when attempting to get a value for field main_data on serializer ParentSerializer .尝试获取序列化ParentSerializer main_data上的字段main_data的值时出现ParentSerializer The serializer field might be named incorrectly and not match any attribute or key on the str instance.序列化器字段可能命名不正确,并且与str实例上的任何属性或键都不匹配。 Original exception text was: 'str' object has no attribute 'main_data'.原始异常文本是:“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
# }

Look's like you have two problems:看起来你有两个问题:

  1. Incorrect return value for get_queryset method get_queryset方法的返回值不正确
  2. Passing some extra data to serializer将一些额外的数据传递给序列化程序

As for the first one you simply should not override that method or return a queryset.至于第一个,您根本不应该覆盖该方法或返回查询集。 Depending on how extra data is being computing there can be the tradeoffs like annotating value on queryset and specifying it in serializer fields list .根据计算额外数据的方式,可以进行权衡,例如在查询集上注释值并在序列化器字段列表中指定它。

For the second one - if annotation is not an option and you'd like to calculate value one's, the possible solution is to add extra data to serializer context and use it as return value of serializer method field .对于第二个 - 如果注释不是一个选项并且您想计算一个值,可能的解决方案是将额外的数据添加到序列化器上下文并将其用作序列化器方法 field 的返回值。 In this case extra data will be placed on the same level with model records data (extra field for each model record):在这种情况下,额外的数据将与模型记录数据放在同一级别(每个模型记录的额外字段):

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

Or you can continue using your approach with nested relationships by override list (and create , if needed) - just add extra data to serializer validated data, so that the result will look like this:或者,您可以通过覆盖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' ) 

I think what you want to achieve is to pass some extra data to the ParentSerializer to be returned by the list action on your view.But you have multiple problems in your code:我认为您想要实现的是将一些额外的数据传递给ParentSerializer ,以便您的视图上的list action返回。但是您的代码中存在多个问题:

  1. get_queryset is only used to return an instance of models.QuerySet , which is used to determine which model objects the view is allowed to perform any kind of actions on. get_queryset仅用于返回models.QuerySet的实例,用于确定允许视图对哪些模型对象执行任何类型的操作。
  2. This is a follow up on the first point, get_queryset is not used to pass data to the serializer, this is done during the Serializer initialization.这是对第一点的跟进, get_queryset不用于将数据传递给序列化程序,这是在Serializer程序初始化期间完成的。
  3. You are using ModelSerializer but you're not specifying the fields attribute on the Meta class which is not allowed anymore since drf version 3.3 , you have to define either a fields attribute or an exclude attribute.您正在使用ModelSerializer但您没有在Meta类上指定fields属性,因为drf version 3.3不再允许,您必须定义fields属性或exclude属性。

As for actually how to pass that extra data to the Serializer, you can do that in multiple ways:至于实际上如何将额外数据传递给序列化程序,您可以通过多种方式做到这一点:

  1. define a SerializerMethodField as proposed by Charnel in his answer which is described by the drf docs here , your serializer will look like this:定义一个由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

Note that this field is a read only field.请注意,此字段是只读字段。

  1. You can inject the calculated value to the end result during validation, this will make your serializer look like this:您可以在验证期间将计算值注入最终结果,这将使您的序列化程序如下所示:
    class ParentSerializer(serializers.Serializer):
        main_data = MainDataSerializer(many=True)

        def validate(self, attrs):
            attrs["extra_data"] = some_calculated_value()
            return attrs
  1. You can also do it by overriding list function on the View and change the data passed to the serializer.您还可以通过覆盖视图上的list函数并更改传递给序列化程序的数据来实现。

I personally prefer option number 1 since SerializerMethodField fits the purpose, while the validate function should be used only for validation to abide by the single responsibility concept.我个人更喜欢选项 1,因为SerializerMethodField符合目的,而validate函数应该仅用于验证以遵守单一职责概念。

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

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