Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to order a Django Rest Framework ManyToMany related field?

I have a Django Rest Framework application with the following (simplified) models.py:

class Photo(models.Model):
    ...

class Album(models.Model):
    ...
    photos = models.ManyToManyField(Photo, through='PhotoInAlbum', related_name='albums')

class PhotoInAlbum(models.Model):

    photo = models.ForeignKey(Photo)
    album = models.ForeignKey(Album)

    order = models.IntegerField()

    class Meta:
        ordering = ['album', 'order']

And in my serializers.py, I have the following:

class AlbumSerializer(serializers.ModelSerializer):

    ...
    photos = serializers.PrimaryKeyRelatedField('photos', many=True)

My question is, how can I have AlbumSerializer return the photos ordered by the field order?

like image 758
Ofirov Avatar asked Aug 05 '14 08:08

Ofirov


2 Answers

The best solution to customise the queryset is using serializers.SerializerMethodField, but what shezi's reply is not exactly right. You need to return serializer.data from SerializerMethodField. So the solution should be like this:

class PhotoInAlbumSerializer(serialisers.ModelSerializer):
    class Meta:
        model = PhotoInAlbum


class AlbumSerializer(serializers.ModelSerializer):
    # ...

    photos = serializers.SerializerMethodField('get_photos_list')

    def get_photos_list(self, instance):
        photos = PhotoInAlbum.objects\
            .filter(album_id=instance.id)\
            .order_by('order')\
            .values_list('photo_id', flat=True)

        return PhotoInAlbumSerializer(photos, many=True, context=self.context).data
like image 64
Enix Avatar answered Sep 18 '22 00:09

Enix


It looks as if the RelatedManager that handles the relationship for ManyToManyFields does not respect ordering on the through model.

Since you cannot easily add an ordering parameter to the serializer field, the easiest way to achieve ordering is by using a serializer method:

class AlbumSerializer(serializers.modelSerializer):
    # ...

    photos = serializers.SerializerMethodField('get_photos_list')

    def get_photos_list(self, instance):
        return PhotoInAlbum.objects\
            .filter(album_id=instance.id)\
            .order_by('order')\
            .values_list('photo_id', flat=True)
like image 36
shezi Avatar answered Sep 20 '22 00:09

shezi