具有多对多关系的 DRF 2 向嵌套序列化

DRF 2-way nested serialization with many to many relationship

我的 StudentMacro 模型之间存在 多对多 关系, 使用中介模型

class Person(models.Model):
    # cf/NIN optional by design
    cf = models.CharField(_('NIN'), unique=True, blank=True, null=True, max_length=16)
    first_name = models.CharField(_('first name'), blank=False, max_length=40)
    last_name = models.CharField(_('last name'), blank=False, max_length=40)
    date_of_birth = models.DateField(_('date of birth'), blank=False)

    class Meta:
        ordering = ['last_name', 'first_name']
        abstract = True

    def __str__(self):
        return self.first_name + ' ' + self.last_name


class Macro(models.Model):
    name = models.CharField(_('name'), unique=True, blank=False, max_length=100)
    description = models.TextField(_('description'), blank=True, null=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class Student(Person):
    enrollment_date = models.DateField(_('enrollment date'), blank=True, null=True)
    description = models.TextField(_('description'), blank=True, null=True)
    macro = models.ManyToManyField(Macro, through='MacroAssignement')


class MacroAssignement(models.Model):
    student = models.ForeignKey(Student, related_name='macros', on_delete=models.CASCADE)
    macro = models.ForeignKey(Macro, related_name='students', on_delete=models.CASCADE)

    def __str__(self):
        return str(self.student)

我配置序列化程序以便在序列化学生时利用嵌套序列化

class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id',
                  'cf',
                  'first_name',
                  'last_name',
                  'date_of_birth')
        abstract = True


class StudentSerializer(PersonSerializer):
    macro = serializers.StringRelatedField(many=True, read_only=True)

    class Meta(PersonSerializer.Meta):
        model = Student
        fields = PersonSerializer.Meta.fields + ('enrollment_date',
                                                 'description',
                                                 'macro')
        extra_kwargs = {'enrollment_date': {'default': date.today()},
                        'description': {'required': False}}


class MacroSerializer(serializers.ModelSerializer):
    students = StudentSerializer(many=True, read_only=True)

    class Meta:
        model = Macro
        fields = ('id',
                  'name',
                  'description',
                  'students')

直到这里没问题,当我请求student数据时,macro相关信息也随之而来。这是一个例子

    {
        "id": 18,
        "cf": "ciaciacia",
        "first_name": "Paolo",
        "last_name": "Bianchi",
        "date_of_birth": "2020-05-01",
        "enrollment_date": null,
        "description": null,
        "macro": [
            "macro1"
        ]
    },

现在,相反,当我请求一个 macro 时,我还想查看相关的 students 列表。我也尝试在 MacroSerializer

中实现嵌套序列化
class MacroSerializer(serializers.ModelSerializer):
        students = StudentSerializer(many=True, read_only=True)

这不起作用,因为我收到以下错误

AttributeError: Got AttributeError when attempting to get a value for field `first_name` on serializer `StudentSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `MacroAssignement` instance.
Original exception text was: 'MacroAssignement' object has no attribute 'first_name'.

[注意:first_nameStudent模型继承自Person模型的字段]

当然我可以实现一个函数来查询数据库并获取分配给给定宏的学生的姓名,但我想知道是否有内置的 django 方法来执行此操作。有点像 2 向嵌套序列化

如之前的有用评论所述,修复是关于使用 related_name。我的代码修改如下

serializers.py

class StudentSerializer(PersonSerializer):
    macro = serializers.StringRelatedField(many=True, read_only=True)

    class Meta(PersonSerializer.Meta):
        model = Student
        fields = PersonSerializer.Meta.fields + ('enrollment_date',
                                                 'description',
                                                 'macro')
        extra_kwargs = {'enrollment_date': {'default': date.today()},
                        'description': {'required': False}}

class MacroSerializer(serializers.ModelSerializer):
    students = StudentSerializer(many=True, read_only=True)

    class Meta:
        model = Macro
        fields = ('id',
                  'name',
                  'description',
                  'students')

models.py

class Student(Person):
    enrollment_date = models.DateField(_('enrollment date'), blank=True, null=True)
    description = models.TextField(_('description'), blank=True, null=True)
    macro = models.ManyToManyField(Macro, through='MacroAssignement', related_name='students')

请注意,我在问题中写的实际上是一个糟糕的设计决策,因为它会导致循环依赖。事实上,StudentSerializer 需要 MacroSerializer,反之亦然。我强烈建议阅读 this question 关于序列化程序中的依赖关系

************ 已编辑 ************

快速修复:在相关模型序列化器中设置depth = 1(在本例中,StudentSerializer)感谢@neverwalkaloner 的回答