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

Mis*_*tyD 4 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',)
Run Code Online (Sandbox Code Playgroud)

这些是模型

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)
Run Code Online (Sandbox Code Playgroud)

我的问题在问题序列化程序中的语句中

answers = EmbeddedAnswerSerializer(many=True,source='answer_set')
Run Code Online (Sandbox Code Playgroud)

many = Trueand source='answer_set'的目的是什么?我从文档中阅读了以下关于many=True

您仍然可以对序列化程序类使用 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'.
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释什么many=True和什么source领域?

rti*_*dru 6

通过@neverwalkaloner 添加到上面的答案

许多 = 真

many=True表示有多个对象(一个可迭代对象)被传递给序列化程序。反过来传递这一领域触发many_initBaseSerializer,自动将创建一个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)
Run Code Online (Sandbox Code Playgroud)

来源 = "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('.')
Run Code Online (Sandbox Code Playgroud)

最后,使用sourcesource_attrs在 中声明的值如下获得bind

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:
Run Code Online (Sandbox Code Playgroud)

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

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

answer_setRelatedManagerDjango 给出的默认名称。在 Django 模型中使用 related_name 命名您的反向关系可能是明智的。这可以通过更改来实现:

question = models.ForeignKey(Question,on_delete=models.PROTECT, related_name='answers')
Run Code Online (Sandbox Code Playgroud)


nev*_*ner 5

可能不是最好的解释,有人可以添加更多详细信息,但简要地many=True告诉序列化程序它将需要用于序列化过程的对象列表。换句话说,它只是一个触发器,允许您指定是一次序列化多个对象,还是仅序列化单个对象。

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

在实践中这条线

answers = EmbeddedAnswerSerializer(many=True, source='answer_set')
Run Code Online (Sandbox Code Playgroud)

意味着你要序列化answer_set的属性Question与对象EmbeddedAnswerSerializer。由于answer_set 是对象列表,您应该将其添加 many=True为参数,以使序列化程序知道它将使用对象列表而不是单个对象。