[Django]-How can I get the parent object in Django Rest Framework serializer

25👍

You can do this by creating a field on your TrackSerializer that has a custom source that retrieves the album name.

The name of the attribute that will be used to populate the field. May be a method that only takes a self argument, such as URLField(‘get_absolute_url’), or may use dotted notation to traverse attributes, such as EmailField(source=’user.email’).

So in your case, the field album_name would need to be added with the custom source set

class TrackSerializer(serializers.ModelSerializer):
    album_name = serializers.CharField(read_only=True, source="album.name")

    class Meta:
        model = Track
        fields = ("album_name", )

This will include the album name in the output under the key album_name.

9👍

To include the parent relation you only need to include its serializer and include it in the fields list.

Class TrackSerializer(ModelSerializer):
    album = AlbumSerializer()

    class Meta:
         model = Track
         fields = ('name', 'year', 'album',)

2👍

I wanted to create a browseable api for one of my Django 2.0 projects, one that would allow a api user to drill down with hyperlinks into the children and then back again, or even start with a child and find the parent or grandparent through a hyperlinks. Also, instead of using the pk number, I wanted to use a slug field for finding the relations.

I could not find any complete solution for what I wanted to do, just bits and pieces, but I was finally able to putting a working api together. I hope that someone will find the solution that, extends the ‘Album – Track’ example, useful.

I apologise for all the code, but by copying and pasting you should be able to put together a working example.

This is this content in my models.py file. Please note the “related_name” parameter in the ForeignKey-fields. It is really important to make the whole setup work.

#models.py
class Artist(models.Model):
    name = models.CharField(max_length=100, blank=False)
    slug = models.SlugField(unique=True, blank=False)

class Album(models.Model):
    artist = models.ForeignKey(Artist, blank=False, 
                    on_delete=models.SET_DEFAULT, 
                    default=1, related_name='albums')
    name = models.CharField(max_length=100, blank=False)
    year = models.IntegerField()
    slug = models.SlugField(unique=True, blank=False)

class Track(models.Model):
    artist = models.ForeignKey(Artist, blank=False, 
                     on_delete=models.SET_DEFAULT, default=1, 
                     related_name='artist_tracks')
    album = models.ForeignKey(Album, blank=True, null=True,  
                     on_delete=models.SET_NULL, 
                     related_name='album_tracks')
    name = models.CharField(max_length=100)
    year = models.IntegerField()

Below is serializers.py. This is where I struggled the most with the error Could not resolve URL for hyperlinked relationship using view name "artist-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field. Resolving the children was quote easy and is facilitated by the model. The key to getting the url for the parents lie in the queryset parameter of the HyperlinkRelatedField serializer along with lookup_field= 'slug'. Below is my serializer classes.

#serializers.py
class ArtistSerializer(serializers.HyperlinkedModelSerializer):
    albums = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='album-detail',
        lookup_field='slug')
    class Meta:
        model = Artist
        fields = ('url', 'name', 'slug', 'albums') #, 'artist_tracks'
        lookup_field = 'slug',
        extra_kwargs = {
            'url': {'lookup_field': 'slug'}
        }

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
    artist = serializers.HyperlinkedRelatedField(
        queryset=Artist.objects.all(),
        lookup_field='slug',
        view_name='artist-detail'
    )
    album_tracks = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='track-detail'
    )
    class Meta:
        model = Album
        fields = ('url', 'name', 'artist', 'album_tracks')
        lookup_field = 'slug',
        extra_kwargs = {
            'url': {'lookup_field': 'slug'}
        }

class TrackSerializer(serializers.HyperlinkedModelSerializer):
    artist = serializers.HyperlinkedRelatedField(
        queryset=Artist.objects.all(),
        lookup_field='slug',
        view_name='artist-detail'
    )
    album = serializers.HyperlinkedRelatedField(
        queryset=Album.objects.all(),
        lookup_field='slug',
        view_name='album-detail'
    )
    class Meta:
        model = Track
        fields = ('url', 'name', 'artist', 'album')

Here the content of my urls.py.

#urls.py
urlpatterns = [
    path('artist/', ArtistList.as_view(), name='artist-list'),
    path('artist/<slug:slug>/', ArtistDetail.as_view(), name='artist-detail'),
    path('album/', AlbumList.as_view(), name='album_list'),
    path('album/<slug:slug>/', AlbumDetail.as_view(), name='album-detail'),
    path('track/', TrackList.as_view(), name='track-list'),
    path('track/<int:pk>/', TrackDetail.as_view(), name='track-detail'),
]

Next views.py. Please note the lookup_field='slug' in the ...Detail(RetrieveUpdateDestroyAPIView): classes.

class ArtistList(ListCreateAPIView):
    queryset = Artist.objects.all()
    serializer_class = ArtistSerializer

class ArtistDetail(RetrieveUpdateDestroyAPIView):
    queryset = Artist.objects.all()
    serializer_class = ArtistSerializer
    lookup_field = 'slug'

class AlbumList(ListCreateAPIView):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer

class AlbumDetail(RetrieveUpdateDestroyAPIView):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer
    lookup_field = 'slug'

class TrackList(ListCreateAPIView):
    queryset = Track.objects.all()
    serializer_class = TrackSerializer

class TrackDetail(RetrieveUpdateDestroyAPIView):
    queryset = Track.objects.all()
    serializer_class = TrackSerializer

Using this approach I was able to product the following JSON for tracks from where one can find both the related artist and album:

{
"url": "http://127.0.0.1:8000/api/track/4/",
"name": "Lifeline",
"artist": "http://127.0.0.1:8000/api/artist/neal-morse/",
"album": "http://127.0.0.1:8000/api/album/lifeline/"
}

And for the albums I am able to generate JSON that looks like below, which allows one to find the artist and all the tracks of the album.

{
"url": "http://127.0.0.1:8000/api/album/lifeline/",
"name": "Lifeline",
"artist": "http://127.0.0.1:8000/api/artist/neal-morse/",
"album_tracks": [
    "http://127.0.0.1:8000/api/track/4/",
    "http://127.0.0.1:8000/api/track/5/",
    "http://127.0.0.1:8000/api/track/6/"
]
}

0👍

You can do it this way (making use of Python dynamism):

def create_serializer(target_class, fields):
    class ModelSerializer(serializers.ModelSerializer):

        class Meta:
            model = target_class
            fields = fields

    return ModelSerializer

So, a Track serializer can be created using this approach:

TrackSerializer = create_serializer(Track, ('album__name',))

Also, be careful of (‘album__name’), which is a parenthesized string expression and not a tuple as you probably intended. To declare it as a tuple, add a comma after it, like this:

fields = ('album__name',)
👤avenet

-4👍

Looks like you need You need Serializer relations.

It also appears a question very similar to this has been asked already: django rest framework view does'nt show related table data

👤Shane

Leave a comment