需要帮助了解序列化程序中的许多和源字段

时间:2018-04-23 08:36:12

标签: django django-rest-framework

我目前正在尝试熟悉DRF,并在完成教程时使用了这些序列化程序

class EmbeddedAnswerSerializer(serializers.ModelSerializer):
    votes = serializers.IntegerField(read_only=True)

    class Meta:
        model = Answer
        fields = ('id', 'text', 'votes',)


class QuestionSerializer(serializers.ModelSerializer):
    answers = EmbeddedAnswerSerializer(many=True,source='answer_set')
    class Meta:
        model = Question
        fields = ('id', 'answers', 'created_at', 'text', 'user_id',)

这些是模型

class Question(models.Model):
    user_id = models.CharField(max_length=36)
    text = models.CharField(max_length=140)
    created_at = models.DateTimeField(auto_now_add=True)


class Answer(models.Model):
    question = models.ForeignKey(Question,on_delete=models.PROTECT)
    text = models.CharField(max_length=25)
    votes = models.IntegerField(default=0)

我的问题出在问题序列化器的声明中

answers = EmbeddedAnswerSerializer(many=True,source='answer_set')

many = True和来源=' answer_set'的目的是什么? ? 我从文档中读到了关于many=True

的以下内容
  

您仍然可以对序列化程序类使用many = True参数。   值得注意的是,许多=真正的论点透明地创造了一个   ListSerializer实例,允许列表和的验证逻辑   要在REST框架代码库中干净地分隔的非列表数据。

我对这意味着什么感到困惑?如果我从代码中删除many=True,我会收到错误

AttributeError at /api/quest/1/2/
Got AttributeError when attempting to get a value for field `text` on serializer `EmbeddedAnswerSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `RelatedManager` instance.
Original exception text was: 'RelatedManager' object has no attribute 'text'.

任何人都可以解释many=True做了什么以及source字段做了什么?

2 个答案:

答案 0 :(得分:4)

可能不是最好的解释,有人可以添加更多详细信息,但简要many=True告诉序列化器它将采用对象列表进行serilizing proccess。换句话说,它只是一个触发器,允许您指定是否一次序列化,以及多个对象,或只是单个对象。

另一方面

source指定对象的哪个属性应该使用当前序列化程序字段进行序列化。

实践中这一行

answers = EmbeddedAnswerSerializer(many=True, source='answer_set')

表示您要使用answer_set序列化Question对象的EmbeddedAnswerSerializer属性。由于answer_set 是对象列表,因此您应添加 many=True作为参数,以使序列化程序知道它将与对象列表而不是单个对象一起使用。

答案 1 :(得分:3)

通过@neverwalkaloner

添加上述答案

很多=真

many=True表示有多个对象(可迭代)被传递给序列化程序。传递此字段会触发many_init内的BaseSerializer自动创建ListSerializer实例。

源代码段:

def __new__(cls, *args, **kwargs):
    # We override this method in order to automagically create
    # `ListSerializer` classes instead when `many=True` is set.
    if kwargs.pop('many', False):
        return cls.many_init(*args, **kwargs)
    return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)

来源=" xyz"

这告诉DRF哪个对象属性提供字段的值。默认假设是在序列化程序上声明的字段名称与提供该值的对象实例上的字段相同。如果不是这样,source允许您显式提供序列化程序将查找值的对象实例。这里可以看到def bind(self, field_name, parent)内部serializers.fields发生这种情况

源代码段:

    # self.source should default to being the same as the field name.
    if self.source is None:
        self.source = field_name

    # self.source_attrs is a list of attributes that need to be looked up
    # when serializing the instance, or populating the validated data.
    if self.source == '*':
        self.source_attrs = []
    else:
        self.source_attrs = self.source.split('.')

最后,使用source中声明的source_attrsbind获取以下值:

def get_attribute(self, instance):
    """
    Given the *outgoing* object instance, return the primitive value
    that should be used for this field.
    """
    try:
        return get_attribute(instance, self.source_attrs)
    except (KeyError, AttributeError) as exc:

假设Question可以有多个Answers,那么您的方法是正确的。

问题似乎是您提供的源是RelatedManager实例本身,而不是Answer对象的查询集。我假设DRF准确地解决了这个问题,您可以尝试将其更改为source =' answer_set.all'。

answer_set是Django提供的默认RelatedManager名称。在Django模型中使用related_name命名您的后向关系可能是明智的。这可以通过更改:

来实现
question = models.ForeignKey(Question,on_delete=models.PROTECT, related_name='answers')