Advertisement
  1. Code
  2. Python
  3. Django

Beginner's Guide to the Django Rest Framework

Scroll to top

So you're learning to use the Django Web Framework, and you're loving it. But do you want an attractive, easy-to-use API for your application? Look no further than the Django Rest Framework (DRF).

The DRF is powerful, sophisticated, and surprisingly easy to use. It offers an attractive, web browseable version of your API and the option of returning raw JSON. The Django Rest Framework provides powerful model serialization, letting you display data using standard function-based views or get granular with powerful class-based views for more complex functionality. All in a fully REST compliant wrapper. Let's dig in.

Laying the Foundation

When working with Python applications, it's always a good idea to sandbox your development with a virtual environment. It helps prevent version collisions between libraries you need in your application and libraries you might already have installed on your machine. It also makes it easy to install dependencies within a virtual env using the requirements.txt file. Lastly, it makes sharing your development environment with other developers a snap.

Envato Tuts+ has two excellent videos on how to install virtualenv and virtualenvwrapper. Take a few minutes to walk through those videos to get virtualenv and virtualenvwrapper installed on your machine. If you've already got them installed, then skip the next section.

Setting Up Your Virtual Environment

The first thing we'll do as part of our application is to set up the virtual environment. First, create a project directory and set up a virtual environment in the directory. 

1
mkdir django_rest_beginners
2
cd django_rest_beginners
3
python3.8 -m venv env

Activate the virtual environment and install the necessary dependencies.

1
pip install Django
2
pip install djangorestframework

Create a Django Project

Create a new Django project.

1
cd django_rest_beginners
2
django-admin.py startproject django_rest .

Create a Django app called bookreview and add the bookreview app and rest_framework to the list of installed apps in the settings.py file.

1
INSTALLED_APPS = [
2
    'django.contrib.admin',
3
    'django.contrib.auth',
4
    'django.contrib.contenttypes',
5
    'django.contrib.sessions',
6
    'django.contrib.messages',
7
    'django.contrib.staticfiles',
8
    'members',
9
    'bookreview',
10
    'rest_framework',
11
]

Create Database Models

Open models.py and add the models for our application.

1
from django.db import models
2
3
# Create your models here.

4
5
6
class Author(models.Model):
7
    objects = models.Manager
8
    first_name = models.CharField(max_length=255)
9
    last_name = models.CharField(max_length=255)
10
    
11
12
    def __str__(self):
13
        return f'Author ({ self.first_name}, {self.last_name})'
14
15
class Book(models.Model):
16
    objects = models.Manager()
17
    title = models.CharField(max_length=255)
18
    isbn = models.CharField(max_length=255)
19
    author = models.ForeignKey(Author, on_delete= models.CASCADE)
20
21
    def __str__(self):
22
        return self.title
23
24

Create Database Migrations for the Models

Migration in the Django application creates actual tables in our database. Make the migrations:

1
python3.8 manage.py makemigrations

You'll see the following output, confirming your migrations file has been created:

1
Migrations for 'bookreview':
2
  bookreview/migrations/0001_initial.py
3
    - Create model Author
4
    - Create model Book

Now you can run the migrate command:

1
python3.8 manage.py migrate

Next, create a superuser:

1
python3.8 manage.py createsuperuser
2
Username: django
3
Email address: 
4
Password: 
5
Password (again): 
6
Superuser created successfully.

Finally, to complete the migration, register the models to the Django admin. Open admin.py and add the following code.

1
from django.contrib import admin
2
from .models import Author,Book
3
4
# Register your models here.

5
6
7
admin.site.register(Author)
8
admin.site.register(Book)

Set Up the Development Server

runserver will start a development server on your local environment.

1
python3.8 manage.py runserver
2
Watching for file changes with StatReloader
3
Performing system checks...
4
5
System check identified no issues (0 silenced).
6
February 23, 2022 - 09:54:08
7
Django version 4.0.1, using settings 'member_rests.settings'
8
Starting development server at https://127.0.0.1:8000/
9
Quit the server with CONTROL-C.
10

Now you can log in to the Django admin page at http://127.0.0.1:8000/admin and add some data.

add Authoradd Authoradd Author

Using the shell, input the following few lines to retrieve an Author record from the database.

1
>>> from bookreview.models import Book,Author
2
>>> author = Author.objects.get(pk=1)
3
>>> author.id
4
1
5
>>> author.first_name
6
'Jane'
7
>>> author.last_name
8
'Austen'
9
>>> 

Similarly, you can retrieve all of the Author records from the database with a different command:

1
>>> authors = Author.objects.all()
2
>>> authors
3
<QuerySet [<Author: Author (Jane, Austen)>, <Author: Author (George, Orwell)>]>
4
>>> 

Unfortunately, this doesn't return data that an AJAX call can understand. So let's add a serializer for authors. Close out the shell by typing quit and add a file bookreview/serializers.py to your project. Add the following code.

1
from rest_framework import serializers
2
from .models import Author
3
4
5
class AuthorSerializer(serializers.ModelSerializer):
6
    """

7
    Serializing all the Authors

8
    """
9
    class Meta:
10
        model = Author
11
        fields = ('id', 'first_name', 'last_name')

Without making any more changes, the serializer gives us quite a lot of power. Head back into the shell, and let's retrieve the author information again.

1
>>> from bookreview.serializers import AuthorSerializer
2
>>> author = Author.objects.get(pk=1)
3
>>> serialized = AuthorSerializer(author)
4
>>> serialized.data
5
{'id': 1, 'first_name': 'Jane', 'last_name': 'Austen'}

Let's add a few more lines of code and see what our API will show us in the browser after our data is run through our new AuthorSerializer

Checking Out the Web Browseable API

Next, open bookreview/views.py and add these lines to the end of the file:

1
from django.shortcuts import render
2
from rest_framework import generics
3
from rest_framework.response import Response
4
from .models import Author
5
from .serializers import AuthorSerializer
6
from rest_framework.generics import ListAPIView
7
8
9
# Create your views here.

10
11
12
class AuthorView(ListAPIView):
13
14
    queryset = Author.objects.all()
15
    serializer_class = AuthorSerializer

Open the root urls.py file and include the bookreview app urls.

1
from django.contrib import admin
2
from django.urls import path,include
3
4
urlpatterns = [
5
    path('admin/', admin.site.urls),
6
    path('books/', include('bookreview.urls'))
7
]

Next, create a file bookreview/urls.py and add the following code.

1
from django.contrib import admin
2
from django.urls import path,include
3
4
urlpatterns = [
5
    path('admin/', admin.site.urls),
6
    path('books/', include('books.urls'))
7
]

The default view for the Django Rest Framework is the APIView. It allows you to define your own GET, PUT, and DELETE methods. It's an excellent way to get base functionality but still have control over the end result. In our case, though, we're letting the DRF do the heavy lifting for us by extending the ListAPIView. We just need to provide a few bits of information to allow the DRF to connect the pieces. We give it the Author model so that it knows how to talk to the database and the AuthorSerializer so that the DRF knows how to return the information. We'll only be working with a few of the built-in API views, but you can read about all of the options on the Django Rest Framework website.

Now that you've made those changes, ensure you've got the server running and then enter the URL http://127.0.0.1:8000/authors/. You should see an attractively designed API view page containing a list of all the authors in the database.

ListAPIViewListAPIViewListAPIView

Giving the Authors Some Books!

While this API view is pretty slick, it's one-for-one with the database. Let's kick up our API view by composing a more complex data set for authors by including a list of all of their books. Open bookreview/serializers.py and add the following line of code before the AuthorSerializer class definition.

1
class BookSerializer(serializers.ModelSerializer):
2
    """

3
    Serializing all the Books

4
    """
5
    class Meta:
6
        model = Book
7
        fields = ('id', 'title', 'isbn','author')

Before adding books to the AuthorSerializer, we have to serialize Books. This should look familiar to you. Because it's almost identical to the AuthorSerializer, we're not going to discuss it.

Next, add the following line immediately after the docstring of the AuthorSerializer class:

1
 books = BookSerializer(read_only=True, many=True, source="book_set")

Then add books to the fields property of the inner Meta class of the AuthorSerializer. The AuthorSerializer should look like this:

1
class AuthorSerializer(serializers.ModelSerializer):
2
    """

3
    Serializing all the Authors

4
    """
5
    books = BookSerializer(read_only=True, many=True, source="book_set")
6
    
7
    class Meta:
8
        model = Author
9
        fields = ('id', 'first_name', 'last_name','books')
10
11
   

Reload the /authors/ endpoint, and you should now see an array of books coming in for each author. Not bad for just a few more lines of code, eh?

array of books coming in for each author.array of books coming in for each author.array of books coming in for each author.

Use SerializerMethodField to Create Custom Properties

The serializer is clever. When we indicate which model it should serialize within the inner metaclass, it knows everything about that model: properties, lengths, defaults, etc. Notice that we're not defining any of the properties found on the model directly within the serializer; we're only indicating which fields should be returned to the API in the fields property. Because the DRF already knows about the properties of the model, it doesn't require us to repeat ourselves. If we wanted, we could be explicit in the BookSerializer and add the following lines, and the DRF would be just as happy.

1
title = serializers.Field(source='title')
2
isbn = serializers.Field(source='isbn')

The serializers.Field method allows you to point to an existing property of the model, the source field, and allows you to explicitly name it something else when returning it to the end user. But what about serializers.SerializerMethodField? That allows you to create a custom property that's not directly tied to the model, whose content is the result of a method call. In our case, we will return a URL containing a list of places you could go to purchase the book. Let's add that custom method now.

Immediately after the docstring of the BookSerializer, add the following string:

1
search_url = serializers.SerializerMethodField('get_search_url')

Then, after the class Meta definition of the BookSerializer, add the following lines:

1
def get_search_url(self, obj):
2
    return "http://www.isbnsearch.org/isbn/{}".format(obj.isbn)

Lastly, we need to add our new property, to the list of fields. Change this:

1
fields = ('id', 'title', 'isbn')

to this:

1
fields = ('id', 'title', 'isbn', 'search_url')

Reload the /authors/ endpoint, and you should now see a URL coming back along with the other information about the book.

URL and other information providedURL and other information providedURL and other information provided

Adding an Author Endpoint

We already have a list of authors, but it would be nice for each author to have their own page. Let's add an API endpoint to view a single author. Open urls.py and add the following line after the author-list route:

1
 path('authors/<int:pk>', AuthorInstanceView.as_view(), name='author-instance')

Then open views.py and add the following lines after the AuthorView class:

1
class AuthorInstanceView(generics.RetrieveAPIView):
2
    """

3
    Returns a single author.

4
    Also allows updating and deleting

5
    """
6
    queryset = Author.objects.all()
7
    serializer_class = AuthorSerializer

Navigate to the author with id 1 at http://127.0.0.1:8000/bookreview/authors/1, and you'll see the following:

author instanceauthor instanceauthor instance

Saving Data: Let the DRF Work for You!

Until now, our app has been read-only. It's time to start saving some data. Create a template file in the directory templates/bookreview/index.html and add the following lines underneath the Authors header:

1
<h2>Authors</h2>
2
3
<ul>
4
    {% for author in authors %}
5
        <li>
6
            <!-- this URL is built manually -->
7
            <a href="/authors/{{author.id}}/">{{author.first_name}} {{author.last_name}}</a>
8
        </li>
9
    {% endfor %}
10
</ul>
11
12
13
<form action="{% url 'author-list' %}" method="post">
14
    <input type="text" name="first_name" />
15
    <input type="text" name="last_name" />
16
    <input type="submit" value="Add Author" />
17
</form>

In views.py, add the following code to serve the index.html page.

1
def index_view(request):
2
    """

3
    Ensure the user can only see their own profiles.

4
    """
5
    response = {
6
        'authors': Author.objects.all(),
7
        # 'books': Book.objects.all(),

8
    }
9
    return render(request, 'bookreview/index.html', response)

Open views.py, change the class that AuthorView extends from generics.ListAPIView to generics.ListCreateAPIView.

1
class AuthorView(ListCreateAPIView):
2
3
    queryset = Author.objects.all()
4
    serializer_class = AuthorSerializer

The index page now looks like this:

index pageindex pageindex page

Then try your request again. When you add a new Author, you will be redirected to this page that shows the author's details. 

POST endpointPOST endpointPOST endpoint

Go back to the main author's page to see your name in lights.

What just happened? The default API view we used only allowed GET requests to the author's endpoint. By changing it to ListCreateAPIView, we told DRF we wanted to also allow POST requests. It does everything else for us. We could just as easily define our own post method within the AuthorView and do some extra stuff there. It might look like this:

1
def post(self, *args, **kwargs):
2
    import pdb; pdb.set_trace()

Keep in mind that while the DRF does enforce database integrity based on the properties of the model, we're not setting any sort of security on who can access or use this form. Diving into security, logging in, and managing permissions is outside the scope of this article, but suffice it to say that DRF does have functionality for allowing access to the views you've been working with, and it's fairly easy to set up.

Finishing Up

You've learned quite a lot about the Django Rest Framework now: how to implement a web-viewable API that can return JSON for you, configure serializers to compose and transform your data, and use class-based views to abstract away boilerplate code. The DRF has more to it than the few bits we were able to cover, but I hope you'll find it useful for your next application.

This post has been updated with contributions from Esther Vaati. Esther is a software developer and writer for Envato Tuts+.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.