2016-03-08 51 views
1

我有以下型号:Django的REST框架:基于父对象上的筛选相关数据

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

和视图集:

class DatasetsViewSet(viewsets.ReadOnlyModelViewSet): 
    serializer_class = DatasetSerializer 

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

我想加载数据集(/api/datasets端点)与他们的部分列表和每个部分的文件列表关联d得到类似的东西:

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

棘手的部分是,给定部分的文件列表应该由父数据集过滤。现在这些部分包含所有文件,而不管他们的数据集。什么是最好的方法来做到这一点?

谢谢!

回答

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') 
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基本URL是/api/files/)将是:

'/api/files/?dataset=1' 
+0

谢谢,但只适用于详细视图(单个数据集)我正在寻找解决方案的细节和列表视图。 – Tristan

+0

@Tristan,你有没有试过它?它将适用于两者。你只是在寻找一个查询参数,然后改变查询是否存在。 –

+0

这不是一个简单的过滤问题,例如我们有一个单独的部分(1),两个数据集:(1和2)以及属于部分1和数据集1的文件。列出数据集时,此文件将列在即使它属于数据集1也是如此。这是因为'section__files'遵循多对多的关系,并且无论文件的'dataset'外键如何,都会从该部分检索所有文件。查看我的答案以获得解决方案。 – Tristan