2

I have the following models:

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)

Serializers:

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

I am trying to load the datasets (/api/datasets endpoint) with the list of their sections and for each section the list of files associated to get something like:

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

The tricky part is that the list of files for a given section should be filtered by the parent dataset. Right now the sections contains all the files regardless of their dataset. What would be the best way to do this?

Thanks!

2 Answers 2

1

Ok so I found a solution I don't know if that's the best way but that worked for me: I modified the serializers to pass the parent object down to the children.

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')
Sign up to request clarification or add additional context in comments.

Comments

0

One way to do this would be to use a query param.

You can override get_queryset to look for this query param in the ViewSet like so:

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

An example URL with a query param (assuming your API base url is /api/files/) would be:

'/api/files/?dataset=1'

3 Comments

Thanks but that would work only for detail view (single dataset) I'm looking for a solution for both detail and list view.
@Tristan, have you tried it for both? It will work for both. You are just looking for a query param, and then altering the query if it exists.
This isn't a simple filtering problem, say we have a single section (1), two datasets: (1 and 2) and a file that belongs to section 1 and dataset 1. When listing the datasets this file will be listed under both dataset 1 and 2 even though it belongs to dataset 1. This is because section__files follows the many to many relation and retrieve all the files from the section regardless to the dataset foreign key of the file. See my answer for a proposed solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.