【问题标题】:DRF nested serializers - Filtering data on child serializersDRF 嵌套序列化器 - 过滤子序列化器上的数据
【发布时间】:2017-07-21 20:54:27
【问题描述】:

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

学校和项目具有多对多关系,因此任何学校都可以订阅任何项目。每所学校都有课程,这些课程是课程的一部分,这就是为什么 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")

以及以下序列化程序:

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'}
        }

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

【问题讨论】:

    标签: django serialization django-rest-framework


    【解决方案1】:

    我知道有两种方法可以做到这一点。第一个是你已经很接近了 编辑:注意到您使用的是相关名称。我已经更新了答案

    class SchoolSerializer( serializers.ModelSerializer):
        programas = ProgramSerializer(source='programs',many=True,read_only=True)
    

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

    您可能还希望在视图中进行一些预取,以获取查询集以最小化查询数。

    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
    

    要获得 PClasses,您只需要过滤您的查询集

    program.classes.filter(school=program.school)
    

    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
    

    编辑 10 个左右:

    由于您已更改程序 -> School 从外键到多对多,这改变了一切。

    对于schoolserializer,您需要使用SerializerMethodField。这样您就可以将额外的context 传递给您的嵌套序列化程序。

    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
    

    【讨论】:

    • 如何让 PClassSerializer 仅显示 School 上正在序列化的 SchoolSerializer 上的课程?
    • 刚刚添加到我的答案底部。
    • 但是 program.school 会返回一个包含所有参加此计划的学校的列表,不是吗?还是我错过了什么?
    • 我应该把程序放在哪里。pclass_set.filter(school=program.school) 对不起,需要更多详细信息……花了一个下午的研究后,我有点迷路了…… .
    • Django 编程的一般提示是除非必要,否则避免使用related_name,因为其他人更容易使用默认的_set 语法。刚刚注意到您所有的相关名称并更新了答案以使用正确的参考
    猜你喜欢
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    相关资源
    最近更新 更多