Search code examples
djangodjango-rest-frameworkdjango-serializer

DRF + SerializerMethodField with Foreign Key


Unlike this question I do not only want to read the data from my SerializerMethodField but also write in it:

Model:

class Artist(models.Model):
    name = models.CharField("name", max_length = 34)
    note = models.CharField("additional info", max_length = 128, blank = True, null = True)

class Album(models.Model):
    name = models.CharField("name", max_length = 34)
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE)

Serializer:

class AlbumSerializer(serializers.ModelSerializer):
    artist = serializers.SerializerMethodField("get_artist")

    def get_artist(self, obj):
        return [obj.artist.pk, obj.artist.name, obj.artist.note]

    class Meta:
        model = Album
        lookup_field = "name"
        fields = ["name", "artist",]
        read_only_fields = ["name",]

ViewSet:

class AlbumViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
    serializer_class = AlbumSerializer
    lookup_field = "name"

    def get_queryset(self):
        return Album.objects.all()

    def retrieve(self, request, artist = None):
        query = Album.objects.filter(artist = artist)
        results = AlbumSerializer(data = query, many = True)
        results.is_valid()
        return Response(results.data)

    def put(self, request, name):
        print("------->", name, request)
        return Response("test")

Getting data from the API works just fine, but when calling api/nevermind I see all the album data but not the dropdown for artist in the browsable API view of DRF.

urls.py:

router = routers.DefaultRouter() 
router.register('albums', views.AlbumViewSet, basename = "albums")             

urlpatterns = [
    path("", views.ListView.as_view(), name = 'index'),
    url('^api/', include(router.urls)),
]

Solution

  • You can't use SerializerMethodField for writing. Read the first sentence in the doc.

    You need to use a nested serializer here. Something like that:

    class ArtistSerializer(serializers.ModelSerializer):
        class Meta:
            model = Artist
            fields = ["id", "name", "note"]
            
    
    class AlbumSerializer(serializers.ModelSerializer):
        artist = ArtistSerializer()
    
        class Meta:
            model = Album
            lookup_field = "name"
            fields = ["name", "artist",]
            read_only_fields = ["name",]
    

    Note that you will need to rewrite the save method of AlbumSerializer in order for writing on the nested serializer to work. Check the doc for more info.

    Getting data from the API works just fine, but when calling api/nevermind I see all the album data but not the dropdown for artist in the browsable API view of DRF.

    I'm pretty sure you won't be able to see any kind of dropdowns for nested serializers or even for slug/pk-related fields. This will require Django to make an additional request to the server/DB to get the data.

    Not sure if I can suggest good ways of having a dropdown like that, I have only ugly ones in mind.