DRF嵌套序列化器-筛选子序列化器上的数据

JLu*_*gao 2 django serialization django-rest-framework

我正在尝试使用嵌套序列化器。如何使用根序列化程序过滤孙子序列化程序上的数据?

学校与计划之间存在多对多关系,因此任何学校都可以订阅任何计划。每所学校都有课程,而这些课程是程序的一部分,这就是PClass对学校和程序都使用外键的原因。

当我调用api ... / api / school / 1时,我想获取学校订阅的所有程序,以及每个程序(在该学校中)提供的课程

class School(TimeStampedModel, SoftDeletableModel):
    name = models.CharField(max_length=40)
    slug = models.SlugField(max_length=40, default='', blank=True)

class Program(TimeStampedModel, SoftDeletableModel):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(max_length=50,default='',blank=True, unique=True)
    description = models.CharField(max_length=100, blank=True)
    school = models.ForeignKey(School, blank=True, null=True, related_name="programs")

class PClass(TimeStampedModel, SoftDeletableModel):
    name = models.CharField(max_length=50)
    slug = models.SlugField(max_length=50,default='',blank=True)
    description = models.CharField(max_length=100)
    program = models.ForeignKey(Program, related_name="classes")
    school = models.ForeignKey(School, related_name="classes")
Run Code Online (Sandbox Code Playgroud)

和以下序列化器:

class SchoolSerializer( serializers.ModelSerializer):
    programs = ProgramSerializer(source='get_programas',many=True,read_only=True)
    class Meta:
        model = School
        fields = '__all__'
        lookup_field = 'slug'
        extra_kwargs = {
            'url': {'lookup_field': 'slug'}
        }

class PClassSerializer(serializers.ModelSerializer):
    class Meta:
        model = Class
        fields = ('name','slug')

class ProgramSerializer(serializers.ModelSerializer):
    school = serializers.SlugRelatedField(queryset=School.objects.all(),
                                            slug_field='name',
                                            required=False)
    classes = PClassSerializer(many=True,read_only=True)
    class Meta:
        model = Program
        exclude = ('id',)
        lookup_field = 'slug'
        extra_kwargs = {
            'url': {'lookup_field': 'slug'}
        }
Run Code Online (Sandbox Code Playgroud)

这可能吗?还是我的模型设置方式有问题?

jon*_*n95 5

我知道有两种方法可以做到这一点。首先是您已经非常接近编辑:注意您正在使用相关名称。我已经更新了答案

class SchoolSerializer( serializers.ModelSerializer):
    programas = ProgramSerializer(source='programs',many=True,read_only=True)
Run Code Online (Sandbox Code Playgroud)

对于更复杂的过滤,最好的方法是使用SerializerMethodField字段。这是一个例子。

您可能还需要在视图中进行一些预提取,以获取queryset以最大程度地减少查询数量。

class SchoolSerializer(serializers.ModelSerializer):
    programas = SerializerMethodField(source='get_programas',many=True,read_only=True)
    class Meta:
        model = Unidade
        fields = '__all__'
        lookup_field = 'slug'
        extra_kwargs = {
            'url': {'lookup_field': 'slug'}
        }

    def get_programas(self, obj):
        # You can do more complex filtering stuff here.
        return ProgramaSerializer(obj.programs.all(), many=True, read_only=True).data
Run Code Online (Sandbox Code Playgroud)

要获取PClass,您只需要使用过滤查询集

program.classes.filter(school=program.school)
Run Code Online (Sandbox Code Playgroud)

ProgramSerializer的完整示例是

class ProgramSerializer(serializers.ModelSerializer):
    classes = SerializerMethodField(source='get_classes', many=True, read_only=True)
    class Meta:
        model = Program

    def get_classes(self, obj):
        return PClassSerializer(obj.classes.filter(school=obj.school), many=True, read_only=True).data
Run Code Online (Sandbox Code Playgroud)

编辑10左右:

由于您已将程序-> School从Foreignkey更改为ManyToMany,因此这将更改所有内容。

对于校书器,您需要使用SerializerMethodField。这样,您可以在额外的上下文中传递给嵌套的序列化程序。

class SchoolSerializer(serializers.ModelSerializer):
    classes = SerializerMethodField(source='get_programs')
    class Meta:
        model = School

    def get_programs(self, obj):
        return ProgramSerializer(obj.program_set.all(), many=True, read_only=True, context={ "school": obj }).data

class ProgramSerializer(serializers.ModelSerializer):
    classes = SerializerMethodField(source='get_classes', many=True, read_only=True)
    class Meta:
        model = Program

    def get_classes(self, obj):
        return PClassSerializer(obj.classes.filter(school=self.context["school"]), many=True, read_only=True).data
Run Code Online (Sandbox Code Playgroud)

  • Django 编程的一般提示是除非必要,否则避免使用 related_name,因为其他人使用默认的 _set 语法更容易。刚刚注意到您所有的 related_names 并更新了答案以使用正确的参考 (2认同)