Reputation: 172
I have 3 django models concatenated by ForeignKey:
# models.py
class Album(models.Model):
some_fields
class Track(models.Model):
some_fields
album = models.ForeignKey(
Album,
related_name='tracks',
on_delete=models.CASCADE,
)
class Comment(models.Model):
some_fields
track = models.ForeignKey(
Track,
related_name='comments',
on_delete=models.CASCADE,
)
I would like to serialize the Album model to view all comments of all its tracks. I have created serializer file like this:
# serializers.py
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = (some_fields, 'comments')
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = (some_fields, 'tracks')
This way I get all the data but in nested lists. I would like to view all the comments of album tracks directly under album object.
# Output albums
[{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values, "comments": [ comment1, comment2 ]},
{ "some_fields": some_values, "comments": []},
{ "some_fields": some_values, "comments": [ comment3 ]},]
},
{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values, "comments": [ comment4, comment5 ]},
{ "some_fields": some_values, "comments": []},
{ "some_fields": some_values, "comments": [ comment6 ]},]
}]
# Desired output albums
[{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values},
{ "some_fields": some_values},
{ "some_fields": some_values},],
"comments": [ comment1, comment2, comment3]
},
{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values},
{ "some_fields": some_values},
{ "some_fields": some_values},],
"comments": [ comment4, comment5, comment6]
}]
I tried to flatten the list directly in serializers file but I get "TypeError: 'ListSerializer' object is not iterable".
# serializers.py
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
comments = [comment for track in tracks for comment in track.comments]
class Meta:
model = Album
fields = (some_fields, 'tracks', 'comments')
Is there any neat way how to output a flattened list directly with serializers? Or should I do it later in views.py? Now it looks simply like this:
# views.py
class AlbumMixin(object):
model = Album
raise_exception = True
serializer_class = AlbumSerializer
def get_queryset(self):
return Album.objects.all()
class AlbumList(AlbumMixin, generics.ListCreateAPIView):
pass
Upvotes: 2
Views: 3907
Reputation: 1531
You can add a serializers.SerializerMethodField
on AlbumSerializer
and return the necessary comments.
Something like this:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = "__all__"
class AlbumSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = (some_fields, "comments", "tracks")
def get_comments(self, obj):
comments = Comment.objects.filter(track__in=obj.tracks.all())
return CommentSerializer(comments, many=True).data
# or you can get rid of CommentSerializer
# return comments.values("some_field")
See SerializerMethodField
docs here
Update
You can improve the query by using the one from @c6754
comments = Comment.objects.filter(track__album_id=obj.id)
Upvotes: 5
Reputation: 887
You could try using a serializerMethodField
# serializers.py
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
comments = serializers.SerializerMethodField()
class Meta:
model = Album
fields = (some_fields, 'tracks', 'comments')
def get_comments(self, obj):
comments = Comments.objects.filter(track__album_id=obj.pk)
return CommentSerializer(comments, many=True).data
Upvotes: 2