Django & EmberJS Full Stack Basics: Connecting Frontend and Backend — Part 3
In Part 3 we use the Django REST Framework to build our books
API.The books
API has serializers, views, and URLs that query, structure, and deliver the book data from the database to API endpoints.
3.1 Django REST Framework
Django REST Framework (DRF) builds on top of Django and is used to build RESTful Web APIs. It provides tools to make the process straightforward. For example, we’ll use the built-in serializers and views instead of building these from scratch.
For more complex projects you’ll want to overwrite defaults or make your own. Again, for the purposes of simplicity and sticking to our primary goal (stitching together the frontend and backend) we’ll use what comes out of the box without undue modification.
3.1.1 Install Django REST Framework
Make sure you’re in the my_library
directory and have the virtual environment activated. To start working with DRF install it with pip
:
# install Django REST Framework
pip install djangorestframework
# install Markdown support for the browsable API
pip install markdown
Now open up my_library/server/server/settings.py
and install DRF right above the book
app.
INSTALLED_APPS = [
...
'rest_framework',
'books'
]
Add the default settings at the bottom of the file:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
This is a permission class that ‘allows unauthenticated users to have read-only access to the API’. Find out more about permissions here.
3.2 The books API
3.2.1 Create the books API folder
With DRF installed let’s start building the books
API. Create a new folder called api
inside books
app with an empty __init__.py
file: my_library/server/books/api/__init__.py
.
This empty file helps Python understand that this folder is a Python module. The api
folder will contain the serializers, views, and URLs for our books data.
3.2.2 Create a serializer
In simple terms, serializers take database data and restructure it to work more easily and efficiently with higher layers of our application like the frontend. For example, the Ember frontend we will create expects the data returned to it from a request to be in the JSON format.
Create the serializer in my_library/server/books/api/serializers.py
:
from rest_framework import serializers
from books.models import Bookclass bookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = (
'id',
'title',
'author',
'description',
)
This serializer takes the data and transforms it into the JSON format so that it’s understandable to the frontend.
Imports
We are importing built-in serializers
from DRF, and the Book
model from our books
app.
The bookSerializer Class
For this project we want a Serializer
class that ‘corresponds to the Model fields’. We can do this easily with the ModelSerializer
. According to the documentation:
The ModelSerializer
class is the same as a regular Serializer
class, except that:
- It will automatically generate a set of fields for you, based on the model.
- It will automatically generate validators for the serializer, such as unique_together validators.
- It includes simple default implementations of
.create()
and.update()
.
As you see, for the purposes of our simple application the built-in tools are more than capable of handling our basic needs.
3.2.3 Create a view
View functions take in a web request (i.e.localhost:8000/api/books
) and return web responses which can be ‘HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an image . . . or anything, really.’ In our case we expect to get back books data structured in the JSON format.
Create the views file in my_library/server/books/api/views.py
:
from rest_framework import generics, mixins
from books.models import Book
from .serializers import bookSerializerclass bookAPIView(mixins.CreateModelMixin, generics.ListAPIView):
resource_name = 'books'
serializer_class = bookSerializer serializer_class = bookSerializer def get_queryset(self):
return Book.objects.all() def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Imports
First we are import generics
and mixins
from DRF. Then the Book
model from our books
app and the bookSerializer
that we just created.
generics
refers to API views that ‘map closely to your database models’. These are ‘pre-built views that provide for commonly used patterns’. mixins
are classes that ‘provide the actions that are used to provide the basic view behavior’. Our book
model is simplistic with only the title
, author
, and description
attributes so these provide us with the basics we need.
The bookAPI View
We then create a bookAPIView
which takes in the CreateModelMixin
and ListAPIView
.
CreateModelMixin
provides a ‘.create(request, *args, **kwargs)
method, that implements creating and saving a new model instance’. It comes with a 201 Create
response, with a ‘serialized representation of the object’ when successful and a 400 Bad Request
response with errors details when not.
ListAPIView
serves our read-only endpoints (GET) and is used to ‘represent a collection of model instances’. This is used when we want to get all or many books.
bookAPIView
also takes in our recently created bookSerializer
for it’s serializer_class
.
We set the resource_name
to ‘books` so ‘to specify the type
key in the json output’. The frontend client data store layer will have a book
model that is case sensitive so setting the resource_name
here nips that issue in the bud.
Functions
The function get_queryset
returns all of the book objects in the database while post
takes in the request and arguments and creates a new database record of a book if the request is valid.
3.2.4 Create URLS
URLs are used to map a URL (i.e. localhost:8000/api/books
) to views.
Create the URLs file in my_library/server/books/api/urls.py
:
from .views import bookAPIView
from django.conf.urls import urlurlpatterns = [
url(r'^$', bookAPIView.as_view(), name='book-create'),
]
Imports
We import our newly created view bookAPIView
and url
. We’ll use url
to create a list of url
instances.
booksAPI URL patterns
In the urlpatterns
array we create a URL pattern with the following structure:
- the pattern
r'^$'
- the Python path to the view
bookAPIView.as_view()
- the name
name='book-create'
The pattern r'^$'
is a regular expression that ‘matches an empty line/string’. This means that a match will be found if localhost:8000
is visited. It matches to anything that comes after the base URL. Note, this will not work if a /
is added to the end of that URL.
We call .as_view()
on bookAPIView
because that ‘is the function(class method) which will connect [the] class with its url’. When a given URL is visited and is matched to the URL pattern that pattern will then return the bookAPI
view that we’ve told it to respond with.
The name='book-create'
attribute provides us with a name
attribute which can be used to refer to our URL throughout our project. If you want to change the URL or the view it refers to, it only has to be changed here. Without name
you’ll have to go through the entire project to update. Check out this thread to find out more.
server URL patterns
Now let’s open up server
’s URLs file my_library/server/server/urls.py
:
from django.conf.urls import url, include
from django.contrib import adminurlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/books', include('books.api.urls', namespace='api-books')),
]
Here we import include
and create the r'^api/books'
pattern which takes in any URLs we created in the api
folder. Now the base URL for our books
API URLs becomes localhost:8000/api/books
. Visiting this URL will match to our r'^/api/books'
pattern which will match to the r'^$'
pattern we constructed in our books
API.
We use namespace='api-books'
so that that URLs don’t collide with each other if they are named the same in another app we might create. Learn more about why namespaces
are used in this thread.
3.2.5 Demonstration: Browsing the books API
Now that we have the base REST framework setup let’s check out the data the backend is returning. With the server running, visit http://localhost:8000/api/books
. The browsable API should return something like this:
3.3 Conclusion
Awesome, we’re really getting going now. At the end of Part 3 we’ve completed the following steps:
- Installed Django REST Framework into our project
- Started building the books API
- Created a
serializer
for books - Created a
view
for books - Created
URLs
for books - Browsed the books API that returns book data from the backend
In Part 4 we will start our EmberJS project which serves as the frontend client. We’ll install the required software, setup a basic DOM, styles, create the book
model, and the books
route. We’ll also load up some fake data to show how real data from our backend will be displayed when we finally access it.