Reputation: 3232
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!
Upvotes: 2
Views: 1837
Reputation: 3232
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')
Upvotes: 1
Reputation: 20760
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'
Upvotes: 0