Reputation: 60
Consider these three models:
class Genre(models.Model):
slug = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=300)
class Group(models.Model):
slug = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=300)
genre = models.ForeignKey('Genre', related_name='groups')
class Album(models.Model):
name = models.CharField(max_length=300)
track_count = models.IntegerField()
artist = models.ForeignKey('Group', related_name='albums')
Now, I need this kind of DRF output:
[
{'name': 'Rock', 'albums': [
{'name': 'Meteora', 'track_count': 12},
{'name': 'Master of Puppets', 'track_count': 10'}]
},
{..},
]
In other words, I need to get all of the albums in each genre, bypassing "group" model. I have achieved something similar this way:
views.py
class SegmentViewSet(viewsets.ModelViewSet):
queryset = Genre.objects.all().annotate(Count('name'))
serializer_class = GenreSerializer
serializers.py
class GroupSerializer(serializers.ModelSerializer):
albums = AlbumSerializer(many=True, read_only=True)
def to_representation(self, value):
return value.products.annotate(Count('id')).values('name', 'sap_code')
class Meta:
model = Group
fields = ('albums',)
class GenreSerializer(serializers.ModelSerializer):
albums = GroupSerializer(many=True, read_only=True)
class Meta:
model = Genre
fields = ('id',
'name',
'albums')
That gives me something like this:
[
{'name': 'Rock', 'albums': [
[
{'name': 'Meteora', 'track_count': 12},
{...},
{...}
],
[
{...},
{...}
]
]
}
]
Which is albums, grouped by albums, grouped by genre. That is a nested array and I need just the albums grouped by genre in one array.
I know that my solution with to_representation function is pretty dirty, but it is something alike to what I'm looking for. But hey, that's what custom related fields are for.
Is there a way to get desired output without direct linking albums to genres? There really IS a reason to keep things this way.
I already tried custom related fields, PrimaryKeyRelatedField, even tried to overload get_queryset in my view. Did I overlook something?
Upvotes: 1
Views: 109
Reputation: 3020
You could try defining serializers as follows:
from rest_framework import serializers
class GenreSerializer(serializers.ModelSerializer):
albums = serializers.SerializerMethodField()
class Meta:
model = Genre
def get_albums(self, obj):
albums = Album.objects.filter(group__genre=obj)
albums_serializer = AlbumSerializer(data=albums, many=True)
albums_serializer.is_valid()
return albums_serializer.data
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
model = Album
The result should be as follows:
[{
'name': 'Rock',
'albums': [{
'name': 'Meteora',
'track_count': 13
},
{
'name': 'Master of Puppets',
'track_count': 12
},
{
'name': 'Nevermind',
'track_count': 11
}
]
},
{
'name': 'Pop',
'albums': [{
'name': 'Ray of Light',
'track_count': 11
},
{
'name': 'Thriller',
'track_count': 14
}
]
}
]
Mind you that this is highly unoptimized and untested but from here I'm sure you know how to use GenreSeralizer
in a view and optimize the code.
Upvotes: 1