如何使用Django Rest Framework包含相关的模型字段?

问题:如何使用Django Rest Framework包含相关的模型字段?

假设我们有以下模型:

class Classroom(models.Model):
    room_number = [....]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

假设不是通过ManyRelatedPrimaryKeyField函数获得这样的结果:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

让它返回包含完整相关模型表示的内容,例如:

{
    "room_number": "42", 
    "teachers": [
        {
           'id':'27,
           'name':'John',
           'tenure':True
        }, 
        {
           'id':'24,
           'name':'Sally',
           'tenure':False
        }, 
    ]
},

这可能吗?如果是这样,怎么办?这是一个坏主意吗?

Let’s say that we have the following model:

class Classroom(models.Model):
    room_number = [....]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

Let’s say that instead of getting a result like this per the ManyRelatedPrimaryKeyField function:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

have it return something that includes the full related model representation like:

{
    "room_number": "42", 
    "teachers": [
        {
           'id':'27,
           'name':'John',
           'tenure':True
        }, 
        {
           'id':'24,
           'name':'Sally',
           'tenure':False
        }, 
    ]
},

Is this possible? If so, how? And is this a bad idea?


回答 0

最简单的方法是使用depth参数

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

但是,这仅包括前向关系的关系,在这种情况下,这并不是您真正需要的,因为教师字段是反向关系。

如果您有更复杂的要求(例如,包括反向关系,嵌套某些字段,但不嵌套其他字段,或者仅嵌套字段的特定子集),则可以嵌套序列化程序,例如…

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

请注意,我们在序列化器字段上使用source参数来指定用作字段源的属性。我们可以通过使用模型上的related_name选项source来确保teachers属性存在,从而删除参数。Teacherclassroom = models.ForeignKey(Classroom, related_name='teachers')

要记住的一件事是,嵌套的序列化程序当前不支持写操作。对于可写表示形式,应使用常规的平面表示形式,例如pk或超链接。

The simplest way is to use the depth argument

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

However, that will only include relationships for forward relationships, which in this case isn’t quite what you need, since the teachers field is a reverse relationship.

If you’ve got more complex requirements (eg. include reverse relationships, nest some fields, but not others, or nest only a specific subset of fields) you can nest serializers, eg…

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

Note that we use the source argument on the serializer field to specify the attribute to use as the source of the field. We could drop the source argument by instead making sure the teachers attribute exists by using the related_name option on your Teacher model, ie. classroom = models.ForeignKey(Classroom, related_name='teachers')

One thing to keep in mind is that nested serializers do not currently support write operations. For writable representations, you should use regular flat representations, such as pk or hyperlinking.


回答 1

谢谢@TomChristie !!!你帮了我很多忙!我想稍微更新一下(由于遇到错误)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

Thank you @TomChristie!!! You helped me a lot! I would like to update that a little (because of a mistake I ran into)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

回答 2

这也可以通过使用打包为drf-flex-fields的非常方便的dandy django来完成。我们使用它,它非常棒。您只需安装它pip install drf-flex-fields,将其通过序列化器,添加expandable_fields并宾果游戏(下面的示例)。它还允许您使用点表示法指定深层嵌套关系。

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

然后,将其添加?expand=teacher_set到您的URL中,并返回扩展的响应。希望有一天能对某人有所帮助。干杯!

This can also be accomplished by using a pretty handy dandy django packaged called drf-flex-fields. We use it and it’s pretty awesome. You just install it pip install drf-flex-fields, pass it through your serializer, add expandable_fields and bingo (example below). It also allows you to specify deep nested relationships by using dot notation.

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

Then you add ?expand=teacher_set to your URL and it returns an expanded response. Hope this helps someone, someday. Cheers!