This content originally appeared on Envato Tuts+ Tutorials and was authored by Andy Matthews
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, 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 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.
mkdir django_rest_beginners cd django_rest_beginners python3.8 -m venv env
Activate the virtual environment and install the necessary dependencies.
pip install Django pip install djangorestframework
Create a the Django Project
Create a new Django project.
cd django_rest_beginners 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.
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'members', 'bookreview', 'rest_framework', ]
Create Database Models
Open models.py and add the models for our application.
from django.db import models # Create your models here. class Author(models.Model): objects = models.Manager first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) def __str__(self): return f'Author ({ self.first_name}, {self.last_name})' class Book(models.Model): objects = models.Manager() title = models.CharField(max_length=255) isbn = models.CharField(max_length=255) author = models.ForeignKey(Author, on_delete= models.CASCADE) def __str__(self): return self.title
Create Database Migrations for the Models
Migration in the Django application creates actual tables in our database. Make the migrations:
python3.8 manage.py makemigrations
You'll see the following output, confirming your migrations file has been created:
Migrations for 'bookreview': bookreview/migrations/0001_initial.py - Create model Author - Create model Book
Now you can run the migrate
command:
python3.8 manage.py migrate
Next, create a superuser:
python3.8 manage.py createsuperuser Username: django Email address: Password: Password (again): Superuser created successfully.
Finally, to complete the migration, register the models to the Django admin. Open admin.py and add the following code.
from django.contrib import admin from .models import Author,Book # Register your models here. admin.site.register(Author) admin.site.register(Book)
Set Up the Development Server
runserver
will start a development server on your local environment.
python3.8 manage.py runserver Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). February 23, 2022 - 09:54:08 Django version 4.0.1, using settings 'member_rests.settings' Starting development server at https://127.0.0.1:8000/ Quit the server with CONTROL-C.
Now you can log in to the Django admin page at http://127.0.0.1:8000/admin and add some data.
![add Author](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/add_author.png)
![add Author](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/add_author.png)
![add Author](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/add_author.png)
Using the shell, input the following few lines to retrieve an Author
record from the database.
>>> from bookreview.models import Book,Author >>> author = Author.objects.get(pk=1) >>> author.id 1 >>> author.first_name 'Jane' >>> author.last_name 'Austen' >>>
Similarly, you can retrieve all of the Author records from the database with a different command:
>>> authors = Author.objects.all() >>> authors <QuerySet [<Author: Author (Jane, Austen)>, <Author: Author (George, Orwell)>]> >>>
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.
from rest_framework import serializers from .models import Author class AuthorSerializer(serializers.ModelSerializer): """ Serializing all the Authors """ class Meta: model = Author fields = ('id', 'first_name', 'last_name')
Without making any more changes, the serializer gives us quite much power. Head back into the shell, and let's retreive the author information again.
>>> from bookreview.serializers import AuthorSerializer >>> author = Author.objects.get(pk=1) >>> serialized = AuthorSerializer(author) >>> serialized.data {'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:
from django.shortcuts import render from rest_framework import generics from rest_framework.response import Response from .models import Author from .serializers import AuthorSerializer from rest_framework.generics import ListAPIView # Create your views here. class AuthorView(ListAPIView): queryset = Author.objects.all() serializer_class = AuthorSerializer
Open the root urls.py file and include the bookreview
app urls.
from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('books/', include('bookreview.urls')) ]
Next, create a file bookreview/urls.py and add the following code.
from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('books/', include('books.urls')) ]
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.
![ListAPIView](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/listapiview.png)
![ListAPIView](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/listapiview.png)
![ListAPIView](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/listapiview.png)
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.
class BookSerializer(serializers.ModelSerializer): """ Serializing all the Books """ class Meta: model = Book 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:
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:
class AuthorSerializer(serializers.ModelSerializer): """ Serializing all the Authors """ books = BookSerializer(read_only=True, many=True, source="book_set") class Meta: model = Author fields = ('id', 'first_name', 'last_name','books')
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?
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/serializer.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/serializer.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/serializer.png)
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.
title = serializers.Field(source='title') 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:
search_url = serializers.SerializerMethodField('get_search_url')
Then after the class Meta
definition of the BookSerializer
add the following lines:
def get_search_url(self, obj): return "http://www.isbnsearch.org/isbn/{}".format(obj.isbn)
Then lastly we need to add our new property, to the list of fields. Change this:
fields = ('id', 'title', 'isbn')
to this:
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.
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/serach_serializer.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/serach_serializer.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/serach_serializer.png)
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:
path('authors/<int:pk>', AuthorInstanceView.as_view(), name='author-instance')
Then open views.py and add the following lines after the AuthorView
class:
class AuthorInstanceView(generics.RetrieveAPIView): """ Returns a single author. Also allows updating and deleting """ queryset = Author.objects.all() 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:
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/author_instance.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/author_instance.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/author_instance.png)
Saving Data: Let the DRF Work for You!
Up 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:
<h2>Authors</h2> <ul> {% for author in authors %} <li> <!-- this URL is built manually --> <a href="/authors/{{author.id}}/">{{author.first_name}} {{author.last_name}}</a> </li> {% endfor %} </ul> <form action="{% url 'author-list' %}" method="post"> <input type="text" name="first_name" /> <input type="text" name="last_name" /> <input type="submit" value="Add Author" /> </form>
In views.py, add the following code to server the index.html page.
def index_view(request): """ Ensure the user can only see their own profiles. """ response = { 'authors': Author.objects.all(), # 'books': Book.objects.all(), } return render(request, 'bookreview/index.html', response)
Open views.py, change the class that AuthorView
extends from generics.ListAPIView
to generics.ListCreateAPIView
.
class AuthorView(ListCreateAPIView): queryset = Author.objects.all() serializer_class = AuthorSerializer
The index page now looks like this:
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/index_page.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/index_page.png)
![](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/index_page.png)
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 endpoint](https://cms-assets.tutsplus.com/cdn-cgi/image/width=850/uploads/users/1885/posts/39934/image-upload/POST.png)
![POST endpoint](https://cms-assets.tutsplus.com/cdn-cgi/image/width=630/uploads/users/1885/posts/39934/image-upload/POST.png)
![POST endpoint](https://cms-assets.tutsplus.com/cdn-cgi/image/width=360/uploads/users/1885/posts/39934/image-upload/POST.png)
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:
def post(self, *args, **kwargs): 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 trivial 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+.
This content originally appeared on Envato Tuts+ Tutorials and was authored by Andy Matthews
![](https://www.radiofree.org/wp-content/plugins/print-app/icon.jpg)
Andy Matthews | Sciencx (2014-02-26T20:22:15+00:00) Beginner’s Guide to the Django Rest Framework. Retrieved from https://www.scien.cx/2014/02/26/beginners-guide-to-the-django-rest-framework/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.