Building the backend of a To Do app.
This tutorial was based on Writing your First Django App and How To Build a To-Do application Using Django and React
Contents:
Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.
Django is a Python-based web application framework that comes fully loaded with modules and features that handle common web development tasks right out of the box. Some examples:
- authentication
- routing
- content administration
- templating
- ORM
- ...
The goal of Django is to allow developers to build out an application as quickly as possible by taking care of as much of the basic boilerplate stuff for you. This makes it a great tool choice for a hackathon or your CMPUT 401 projects.
This tutorial won’t cover all of these features extensively. It’s meant to provide enough information and template code to get you started. If you want to fill in gaps or learn more, there are plenty of resources online, starting with the documentation.
First of all, make sure you have a version of Python installed on your machine. It’s recommended to use the latest version of Python3, but most 3.x.y versions should work. You can get the latest version of Python here: https://www.python.org/downloads/.
Alternatively, you can use pyenv
(https://realpython.com/intro-to-pyenv/) to easily manage multiple Python versions on your machine.
Create a new directory to house your project, and cd into it.
$ mkdir django-todo-backend
$ cd django-todo-backend
When developing a new Python project, it is best practice to create a virtual environment. This helps keep the dependencies needed by the project isolated and separate from your system (and other projects) dependencies.
You can install virtualenv
with pip and check the installation.
$ pip install virtualenv
$ virtualenv --version
Next, we will create the virtualenv for our project. From within the directory you created in the last step, run the following command.
$ virtualenv venv --python=python3.7
This will create a virtualenv folder in our project directory called venv
using Python 3.7. It’s generally a standard practice to name your virtualenv venv
, but you can name it as you like. There should now be a venv
folder in your directory:
Now we can boot up our virtual environment.
# for linux or macOS
source venv/bin/activate
# for windows
venv\Scripts\activate.bat
You should now see your environment name in parenthesis on the left side of your terminal:
To deactivate the environment.
# for linux, macOS,a nd windows
(venv) $ deactivate
Now if I activate my venv and run freeze again:
As you can see, the different environments have different packages installed. If I install another package while running venv
it will only be installed into that environment, keeping my system packages clean.
You can install an official release of Django with pip. Make sure you are in your virtual environment when installing it.
$ python -m pip install Django
Now that our development environment is set up, we can initialize our Django project. This will auto-generate a code base for us to start with.
(venv) $ django-admin startproject todo_backend
This will create a todo_backend
directory in our project directory. We can cd into this new directory and take a look at what was created.
For more details on what each of these files are, check out the official documentation. For now, suffice to say these are the basic settings and functions needed for a Django project. The manage.py
is a command-line utility that manages many aspects of the Django project, as we will soon see. It’s usually best to work from the directory containing this file, so we can quickly run functions without having to specify the path to it, i.e.
(venv) $ python manage.py some_django_command
# vs.
(venv) $ python ../../path/to/manage.py some_django_command
In Django, the “project” is a collection of configurations and apps that define a particular website. An app is a web application that actually does something like a blog system, or in our case, a todo app.
We can easily create a new app for our todo project. We will call it api
as it will serve as the API for our final product.
(venv) $ python manage.py startapp api
This will create a new api
directory in our project.
Next, we can include our app in our project by adding it to the list of installed apps in our project settings. Open the Django project in your preferred IDE or editor, open todo_backend/settings.py
, and the todo app to INSTALLED_APPS
:
# Application definition
INSTALLED_APPS = [
"api",
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
By default, Django will set up the project to use a SQLite database. This is perfectly fine for development or prototype projects like a hackathon, but it is not suitable for a production environment. This means for your 401 projects, you will want to configure your project to use a more appropriate and scalable database, like PostgreSQL. We won’t cover this in this tutorial, but you can refer to the Django documentation for steps on how to do this.
If you look at the todo_backend/settings.py
file you will see a list of apps that were installed in our project when we first initialized it.
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
Some of these applications require a database table, so we need to initialize our database by running the migrations. A migration is essentially a file that defines changes to your database schema (e.g. adding a field to a table, renaming a table, etc.). Django will translate the changes to SQL statements and, run them against your database. We can run these initial migrations with the following command
(venv) $ python manage.py migrate
and we should see a list of migrations that were applied:
There should now be a db.sqlite3
file in our working directory that contains the necessary tables:
Let’s create a model for a todo item. This will essentially define a new table for our database. Open api/models.py
and edit it so it looks like the following:
from django.db import models
class Todo(models.Model):
title = models.CharField(max_length=120)
description = models.TextField()
completed = models.BooleanField(default=False)
This defines a new model called Todo that has three fields: title, description, complete.
Since we’ve defined a new model, we need to create and run a new migration to update our database. With Django, this is easily achieved with the command
(venv) $ python manage.py makemigration api
You should see an output like this:
# ...
operations = [
migrations.CreateModel(
name='Todo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=120)),
('description', models.TextField()),
('completed', models.BooleanField(default=False)),
],
),
]
We can look at the generated SQL statements by running
(venv) $ python manage.py sqlmigrate api 0001
which should output the following:
BEGIN;
--
-- Create model Todo
--
CREATE TABLE "api_todo" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(120) NOT NULL, "description" text NOT NULL, "completed" bool NOT NULL);
COMMIT;
Now we can run the migration as before.
(venv) $ python manage.py migrate
If we check the tables in our database again, we should see the new todo table:
Let’s take a look at the Django Administration site now. First, we need to create a new admin user. Run the following command and follow the onscreen prompts.
(venv) $ python manage.py createsuperuser
Now let’s start up the development server
(venv) $ python manage.py runserver
This will start a development server, where we can view our site at http://127.0.0.1:8000/.
Open your browser to http://127.0.0.1:8000/admin/ and you should see a login form:
Enter the credentials you specified in the step above to log in. You should be greeted with the Django admin index page:
From here, site administrators can easily interact with and manage the models and project data. This is one of the great features of Django - we have a fully functional administration site, complete with an API to manage our data, in just a few terminal commands. Without the use of any frameworks, something like this would require substantial development time.
Notice that our todo model isn’t accessible from the admin page. We need to register the model first. To do this, open the api/admin.py
and add the following:
from django.contrib import admin
from .models import Todo
admin.site.register(Todo)
Head back to the browser and refresh the page. You should now see the todo app:
Click the ‘add’ button to navigate to the creation form and create a new todo item:
After you save the new item, you should see a list of the todo items currently in the database:
Notice how the item in our list is not very descriptive - it simply tells us that it is a Todo object. Let’s fix this by adding a method to our Todo model. Open api/models.py
again, and edit it so it looks as follows:
from django.db import models
class Todo(models.Model):
title = models.CharField(max_length=120)
description = models.TextField()
completed = models.BooleanField(default=False)
def __str__(self):
return self.title
The __str__
method tells Django what to print for the representation of that object. You can add any type of method to a model just as you would for any other Python class (see Django docs for examples).
If you refresh the browser, you should see the new representation of our todo item:
Now that we’ve covered some Django basics and have our project and app set up, let’s start creating our API for the front end of our application to interact with. We’re going to use djangorestframework
- a powerful and flexible toolkit that makes it easy to set up RESTful Web APIs for Django.
You can install an official release of DRF with pip. Make sure you are in your virtual environment when installing it.
$ python -m pip install djangorestframework
We have to add DRF to our list of installed apps, same as we did with our todo app:
# Application definition
INSTALLED_APPS = [
"api",
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework' # add this!
]
Our REST api endpoint is going to return JSON data to the front end, so that it can work with and display the data. To do this, we need a model serializer, which will convert our Python model instance into JSON. Create a new file api/serializers.py
and add the following code:
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ['id', 'title', 'description', 'completed']
This defines that the Todo model should be serialized by converting the id, title, description, and completed fields to JSON.
Let’s now create the endpoint. In Django terms, these are referred to as Views. Open api/views.py
and create a TodoView
class as follows:
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo
class TodoView(viewsets.ModelViewSet):
serializer_class = TodoSerializer
queryset = Todo.objects.all()
That’s it! By specifying the ModelViewSet
we are inheriting the default DRF actions that correspond to the basic CRUD operations - create, read, update, and delete - in just a few lines of code. Just like with Django, DRF has the ability to abstract lots of boilerplate and otherwise redundant coding away, allowing for speedy development. You can of course write your own views functions and extend the default behaviors as needed. For more details, review https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset.
Now that we have an endpoint defined, we need to make it routable so we can access it with an HTTP client. Since we are using the viewset class for our endpoint, DRF can automatically generate the URL configuration for our API. We just have to register it with a router class. Open todo_backend/urls.py
and edit it as follows:
from django.contrib import admin
from django.urls import include, path
from rest_framework import routers
from api import views
router = routers.DefaultRouter()
router.register(r'todo', views.TodoView, 'todo')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
]
This will register our TodoView at the route /api/todo/
.
With the development server running, navigate to http://127.0.0.1:8000/api/todo/. You should see the following:
From this route we can GET a list of all todo items we currently have, and fill out the form and POST to the endpoint to create a new item:
We can also navigate to http://127.0.0.1:8000/api/todo// to perform operations on a specific (existing) item. For example, from http://localhost:8000/api/todo/1/ we can GET that single item, DELETE it, or PUT to update it.
As you can see, DRF comes with a playground where we can easily interact with and test the API through a friendly UI - yet another benefit of this toolset.
-
Postman
Another friendly UI application that can be installed on most OS. Just specify the request method and url and send it. A nicely formatted response will be displayed
-
curl
This is a command line http client. Similarly, just specify the URL and request method and headers. Check out the manual (
curl —manual
) for more details. -
httpi
Another command line tool, but requires Python 3.6 or newer. You can install it with
$ python -m pip install httpie
or homebrew, apt, etc. You can send requests with
$ http http://127.0.0.1:8000/api/todo/
It has an added benifit of formatting the response:
Now that we have a functioning API endpoint for our app, we can move on to developing the front end.