Returning a Grouped List of Objects in Django

Jekayin-Oluwa
6 min readJun 16, 2023
An image depicting a many-to-one relationship between a music album and its child tracks.
Many-to-one relationship

Recently, I had to churn out an endpoint that fetches a list of items. But, the items were objects grouped into categories.

That is to say, each category consists of several child objects, that is, many-to-one relationships. And I wanted to fetch all the objects as grouped items in a list. So, they had to be grouped by their categories.

This post would outline how I achieved the grouped list of objects with the ForeignKey field and related_name attribute in Django models. I will show how I created the many-to-one relationship from the child model to the parent model. You will also see how to use serializers to combine the objects from both models and organize the output returned in the endpoint.

But, firstly, we will refresh our understanding of some basic concepts for readers who might not be fluid with Django RESTful APIs yet. After then, I will go into the main implementation. The following shows the kind of output we’re trying to achieve in this article.


[
{
"AlbumTitle": "Providence",
"tracks": [
{
"TrackTitle": "Running",
"Length": "3.35",
"Album": 1
},
{
"TrackTitle": "LOTR II",
"Length": "3.23",
"Album": 1
}
]
},
{
"AlbumTitle": "Timeless",
"tracks": [
{
"TrackTitle": "Kante",
"Length": "3.15",
"Album": 2
},
{
"TrackTitle": "Over Dem",
"Length": "3.19",
"Album": 2
}
]
},
{
"AlbumTitle": "NSNV",
"tracks": [
{
"TrackTitle": "God Sent",
"Length": "3.33",
"Album": 3
},
{
"TrackTitle": "Catalyst",
"Length": "2.01",
"Album": 3
}
]
}
]

Background Knowledge

  • REST API: REST is a standard for creating application programming interfaces (APIs) for web applications to connect and share resources. It is based on HTTP thereby utilizing Http actions which to determine the pattern and methods for which web applications communicate. For instance, the GET action is used to READ resources via REST APIs while POST is used to CREATE resources. There are other methods and you can learn how to use them from the Mozilla MDN documentation guide on HTTP request methods.
  • Django Rest Framework (DRF): this is a framework built on top of Django to provide out-of-the-box REST functionalities. You can follow the official DRF guide on how to use DRF in your projects.
  • Models: models in Django are a way to easily interact with your Database. A model represents a database table. It has fields that serve as columns in the table. Django is structured to recognize models as sub-components of apps.
  • Views: Views enable you to implement logic and features in your Django apps. They could be the direct receiver of Http actions on your API endpoints. They can also interact with models to fetch, create, update, or delete database items.
  • Serializers: these are used to interoperate between views and models. They ensure you are passing and receiving expected data formats when interacting with the models.
  • Apps: Apps in Django are reusable units of the application. The idea is that you will create apps for different functions or features and you may port the apps to re-use them. Therefore, you may create the models, views, and serializers for a single functionality in an app.

Prerequisites

1. Possess basic knowledge of Django and be familiar with Django REST Framework

2. Have a Django project ready to practice the concept.

3. Have created an app in the Django project. You can use the app for the instructions in this article.

4. Install django-rest-framework and add it to the INSTALLED_APPS list in your settings.py file. You can follow the project setup instructions on the official website to install and set up the framework. It includes the steps to achieve numbers 2 and 3 above.

Create The Models

We will create two models to handle the relationship between music albums and tracks. An album would usually contain multiple tracks which depicts a many-to-one relationship between the tracks and the album.

You can create a file named `models.py` if you don’t have one yet in your app. Then, go ahead to create the models as shown in the following code

# models.py
from django.db import models


class Album(models.Model):
id = models.AutoField(primary_key=True)
album_title = models.CharField(max_length=50)
artist = models.CharField(max_length=50)
class Meta:
db_table = "Album"


class Track(models.Model):
id= models.AutoField(primary_key=True)
track_title = models.CharField(max_length=100)
length = models.DecimalField(max_digits=5, decimal_places=2)
album = models.ForeignKey(Album, related_name="tracks", on_delete=models.CASCADE)
class Meta:
db_table = "Track"

In the code above:

  • I have the Album model and it has its fields. The model will abstract the Album database table.
  • I also created the Track model and added its fields. However, one of the fields is Album which has a ForeignKey field to link to the other model, Album. The field has an important attribute called related_name. This will be used by the serializer to organize the track objects under their respective albums.
  • db_table helps to enforce the table names in the database. Now, you can create and run the migrations.

Create and run migrations with the following command to seed the database tables:

python manage.py makemigrations
python manage.py migrate

Create The Serializers

In this section, you will see how I created two serializers and used them together. Create a new file serializers.py and add the following code inside it.

# serializers.py
from rest_framework import serializers
from .models import Album, Track


class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['track_title', 'length', 'album']


class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
model = Album
fields = ['album_title', 'artist', 'tracks']

What we have done with the above code is to create a serializer for the Tracks model and make it a field under the AlbumSerializer serializer. This way, we can get the objects in the Tracks table and have them in the Album when returning resources. Note that each serializer class references its respective model. We also selected only the fields we want to serialize in each model.

We may then create views after completing the serializers.

Create The Views

Create a new file views.py if you don’t have it yet and add the following code to it.

# views.py
from rest_framework.generics import ListCreateAPIView
from .models import Album, Track
from .serializers import AlbumSerializer


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

In the code above, we created a view AlbumGroupView based on the generic ListCreateAPIView in DRF. We added a queryset and the AlbumSerializer class.

Now, we will set up a URL that will use the AlbumGroupView.

Set Up The URL Path

Create a urls.py file if you don’t have one already. Then, add the following code to the file.

# urls.py
from django.urls import path
from .views import AlbumGroupView


urlpatterns = [
path('get-albums/', AlbumGroupView.as_view(), name='album_list')
]

The URL path added to the urlpatterns list above links to the AlbumGroupView view and it serves as the endpoint where our users can access the application to get a list of music tracks.

Test The Application

Now, you can test the application. For the sake of simplicity, you can use the Browsable API.

Firstly, you can use the Django Admin utility to add tracks and albums to your database. You can also create separate endpoints with the ListCreateAPIView generic view to add the tracks and albums.

Next, run the server with the runserver command and navigate to the endpoint on your browser. Then, you can see something like the following

The endpoint should return an output similar to what’s shown in the image below.

Screenshot of the API endpoint output. It shows the tracks grouped as objects belonging to their Albums.
Screenshot of the API endpoint output

Conclusion

In this article, I’ve briefly shown you how to use the foreign key field and serializers to obtain the list of objects grouped into categories. Hopefully, it would come in handy when you need to implement it in your project.

Thanks.

If you find this article interesting, helpful, or needing improvement, give it some claps 👏👏👏 and say ‘hi!’ on Twitter @JKYNLW

--

--