The Simplest Django REST Framework Example
Django REST Framework (DRF) is a powerful and flexible toolkit for creating APIs, and the documentation is extensive. However, it can be challenging to choose among all the options to create a simple implementation that gets your API up and running quickly.
This article presents the most automated approach for implementing DRF in a simple but complete example that will get you started right away. All the code we’ll be walking through can be found in this GitHub repository.
Like most Django projects, we need to set up a data model, add some views, and wire them up to urls. Let’s get started!
Let’s Begin with our Data Model
Our example application will provide an API for our game collection. Since this is the simplest example, we’ll model only this single table.
This structure is implemented in models.py:
# Django models for the Games app
from django.db.models import CharField, IntegerField, Model, TextFieldclass Game(Model):
Game in our collection
name = CharField(max_length=500)
description = TextField()
notes = TextField()
rating = IntegerField()
This is standard Django fare, we are just defining a few fields on our Game model. DRF will use this to create the definition of our API.
Now the views — where the magic happens
In views.py, we’re implementing the bulk of our API definition:
# Django and DRF views for Game app
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ReadOnlyModelViewSet
from games.models import Game
Class that converts models to JSON
model = Game
fields = '__all__'
API for Games
queryset = Game.objects.all()
serializer_class = GameSerializer
This is the secret to quickly implementing Django REST Framework — use ModelSerializer and ModelViewSet to implement your API in only a few lines.
In DRF, Serializers do the work of converting data from your database into the JSON format that your API can work with. We’re using a ModelSerializer, which uses the definitions we put into models.py to set up this conversion. The fields = ‘__all__’ configuration is a special DRF construct to include all of the fields from our model in the API, even if we add new ones later.¹
ViewSets automatically create multiple API views for you, representing the most common use cases. Since we’re using ReadOnlyModelViewSet, DRF will automatically create ‘list’ and ‘detail’ views for us.² These views are similar to the Django generic views ListView and DetailView, but they return JSON instead of HTML.
The final step — configuring urls.py
# URLs for Games
from django.urls import include, path
from django.views.generic import RedirectView
from rest_framework import routers
from games.views import GameViewSet
games_api_router = routers.SimpleRouter()
urlpatterns = [
# Automatically Created API URLs from the Router:
# /games/api/ - list of all games (url name is 'game-list')
# /games/api/<pk> - detail of a single game based on its pk
# (url name is 'game-detail')
DRF provides Routers, which automatically create Django URL configurations that wire up to ViewSets. To use them, we create a router, and then register our ViewSet with it under a specified path. We registered GameViewSet under an empty path, so its URLs will appear at the root of where we include them. Finally, we update urlpatterns to include our router urls under our chosen /games/api/ url. You can register multiple ViewSets on the same router under different paths.
We are using SimpleRouter, which is perfect for our app, but also a good choice for most apps.³ You can see the list of its automatically generated URLs in the documentation, and I added some comments in urls.py as quick reference for us in the future. The ‘url name’ is useful for plugging into Django’s reverse function and url template tag. The ‘basename’ of those ‘url names’ is automatically set by the name of our model in the ViewSet, but can be overridden.
We also have a Django RedirectView at the top of urlpatterns to redirect any attempts to get at the root of our app over to our API (since this app is only an API).
Accessing the API
We’ve done it! We created a functioning REST API in less than 20 lines of code in three files. Don’t forget to run manage.py makemigrations and manage.py migrate if you haven’t yet. You can also set up the Django admin to edit your game collection, or use the supplied fixture in the GitHub to load sample data (manage.py loaddata games/fixtures/initial_data.json).
Now it’s time to test it out!
If you are using the example code from the GitHub, here’s your test URL:
You can see that this is a nice way to look at your API. This display is automatically generated by DRF when you access your API directly in a browser. If you access your API via AJAX, it will return only raw JSON. You can see this raw JSON in your browser by appending the ?format=json parameter:
You can get the detail for a single game by appending its primary key:⁴
Note that ModelSerializer is returning quoted strings in the JSON for text and character fields, but unquoted numbers for the Integer field and primary key.
You can customize your ModelViewSet to return a different Serializer for List and Detail (aka retrieve) views, so you can limit the amount of data returned in the List view. But for our simplest example, they both return the same fields.
The “API for Games” text is coming from the comment we made at the top of the ViewSet class definition.
Congratulations, you’ve worked your way through the Simplest Django REST Framework example! Once you have mastered these simple techniques, you can enable a REST API in just a few lines of code.
¹ Using __all__ in development is quick and easy, and in the spirit of this app. But it can be risky to use __all__ in a production application, both because you might accidentally expose data you didn’t mean to in the future, as well as potentially including big fields that bloat out your API responses and slow things down. Once you are ready to move your app to production, it is wise to replace __all__ with a specific list of only the fields your API needs to expose.
² In a future article, we will create an API that enables editing using ModelViewSet. However, a surprising number of API use cases just need to expose some API for consumption by front-end components or partner sites. Consider carefully whether it’s worth the security risk of enabling editing via your API, and default to read-only if you can.
³ There is also DefaultRouter, which is the same as SimpleRouter, but provides one more view that lists out all the APIs registered to it. While this can be useful for creating self-documenting APIs, it is not always desirable to expose a list of all your APIs out to the world, which is why SimpleRouter is often the best choice. You can also create custom routers.
⁴ In production, it’s not always a good idea to expose your auto-generated sequential primary keys via your API. A hacker could append arbitrary numbers to the end of your URLs and get access to data you didn’t intend them to. A certain social network had all its posts (even ‘deleted’ ones) archived this way. In addition, if your database changes, those sequential keys could change and break consumers. In production, it can be smarter to only expose non-sequential keys, like UUIDs or slugs. That said, if your only consumer is your own website, and you are only exposing data that is already public, using sequential keys this way is a time-tested technique. Just think hard about what would happen if someone just tried out every number in sequence.