Django REST框架:基于父对象过滤相关数据

时间:2016-03-08 21:20:34

标签: django django-rest-framework

我有以下型号:

class Section(models.Model):
    name = models.CharField(max_length=255)

class Dataset(models.Model):
    name = models.CharField(max_length=255)
    sections = models.ManyToManyField(Section)

class File(models.Model):
    dataset = models.ForeignKey(Dataset)
    section = models.ForeignKey(Section, related_name='files')
    key = models.CharField(max_length=255)

串行器:

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = ('id', 'key')

class SectionSerializer(serializers.ModelSerializer):
    files = FileSerializer(many=True)

    class Meta:
        model = Section
        fields = ('name', 'files')

class DatasetSerializer(serializers.ModelSerializer):
    sections = SectionSerializer(many=True)

    class Meta:
        model = Dataset
        fields = ('id', 'name', 'sections')

and viewset:

class DatasetsViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = DatasetSerializer

    queryset = Dataset.objects.prefetch_related(
        'sections', 'sections__metric', 'sections__feature', 'sections__files')

我正在尝试使用其部分列表加载数据集(/api/datasets端点),并为每个部分加载与之相关的文件列表:

[
    {
        "id": 1,
        "name": "Q4 2015",
        "sections": [
            {
                "id": 1,
                "name": "Overall Scores"
                "files": [
                    {
                        "id": 1,
                        "key": "this/is/a/path"
                    }
                ]
            }
         ]
    }
]

棘手的部分是,父数据集应该过滤给定部分的文件列表。现在,这些部分包含所有文件,无论其数据集如何。最好的方法是什么?

谢谢!

2 个答案:

答案 0 :(得分:1)

好的,所以我找到了一个解决方案,我不知道这是否是最好的方法,但这对我有用:我修改了序列化程序,将父对象传递给了孩子们。

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = ('id', 'key')

class SectionSerializer(serializers.ModelSerializer):
    files = serializers.SerializerMethodField()
    def get_files(self, obj):
        dataset_id = self.context.get('dataset_id')

        if dataset_id:
            return FileSerializer(many=True).to_representation(
                [f for f in obj.files.all() if f.dataset_id == dataset_id]
                # Using
                #   obj.files.filter(dataset_id=dataset_id)
                # would hit the database for every section making the
                # prefetching useless
            )
        return FileSerializer(many=True).to_representation(obj.files.all())

    class Meta:
        model = Section
        fields = ('name', 'files')

class DatasetSerializer(serializers.ModelSerializer):
    sections = serializers.SerializerMethodField()
    def get_sections(self, obj):
        context = self.context
        context.update({'dataset_id': obj.id})
        return SectionSerializer(many=True, context=context).to_representation(
            obj.sections
        )

    class Meta:
        model = Dataset
        fields = ('id', 'name', 'sections')

答案 1 :(得分:0)

执行此操作的一种方法是使用query param

您可以覆盖get_queryset以在ViewSet中查找此查询参数,如下所示:

def get_queryset(self):
    qs = super(DatasetsViewSet, self).get_queryset()
    dataset = self.request.query_params.pop('dataset', None)
    if dataset:
        qs = qs.filter(dataset=dataset)
    return qs

带有查询参数的示例网址(假设您的API基本网址为/api/files/)将为:

'/api/files/?dataset=1'