具有多对多关系的 DRF 2 向嵌套序列化
DRF 2-way nested serialization with many to many relationship
我的 Student 和 Macro 模型之间存在 多对多 关系, 使用中介模型
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_name
是Student模型继承自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 的回答
我的 Student 和 Macro 模型之间存在 多对多 关系, 使用中介模型
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_name
是Student模型继承自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 的回答