Use Django To Create A Backend Application

Django is a popular Python framework developed to power your backend application. This tutorial will show you how to use Django to create a backend server to feed our Restaurant application.

Quang Nhat Dao
Vibentec-IT
9 min readApr 28, 2022

--

Requirements

  • Django 4.0 supports Python 3.8 or later. For older Python versions you should head to this link below for more information.
  • You should have your favorite IDE already installed on your computer :)

Set-Up

The first thing you need to do is to create a virtual environment.

What is a virtual environment? This is a soft intro for you.
Imagine you have on your computer 2 Python projects A and B. A and B both use package P to realize their functionalities. However, A uses a different version of P other than B does. If you install a fixed version of P on your computer, you won’t be able to run both of A and B simultaneously. That’s why you need to start a virtual environment. In this environment, you will have all the necessary packages for the project. Virtual environments are independent of each other, so you can start a virtual environment for A and another for B and have both A and B run at the same time.

First off, create a directory where your source code should lie.

To create a virtual environment, simply open your command prompt and type:

python -m venv venv

Then type the following to start the virtual environment:

venv\Scripts\activate

If you do it correctly, a string (venv) will appear on the left of your path to the command prompt.

Next, you need to install Django. Type in the following command:

python -m pip install Django

In case you want to share your code with others, it’s best to define what packages are needed for your project. You can do it by creating a requirements.txt file.

pip freeze > requirements.txt

Other people can then start their own virtual environment and install dependencies via:

python -m pip install -r requirements.txt

Great! Now you are done with the setup and can move on to using Django to write our own Backend application.

Create Django Project

First thing first, create a Django project using this command:

django-admin startproject app

You will see a folder with the name app. On your command prompt type the following to change your current directory to the app:

cd app

you need to execute our first migration. Even though no data model has been implemented yet, this step is necessary for the first creation of a superuser. After this, you won’t have to run this code again if you want to create another superuser.

python manage.py migrate

Next thing is to create our first user account

python manage.py createsuperuser

The command prompt will guide you through the registration steps.

Finally, you can start the development server.

python manage.py runserver

Go to http://127.0.0.1:8000/admin/ and log in with your superuser account. If you get to the admin page, then Congratulations! You are now ready to take the next steps to create API endpoints for your backend server.

Create apps

Wait. Didn’t you just create a Django application up there?

Well no. It was a Django project. A project can contain multiple apps. Think of a project as a house. Now what we need are rooms for different purposes. It’s now a good time to explain what our application is about.

The application is called RestaurantApp. It should provide API endpoints, which in turn provide CRUD operations for restaurants, their dishes, and tags from a database. Moreover, different types of users are allowed to different sets of endpoints. Here is a class diagram to illustrate the idea more formally.

You need an app for restaurant-related information and an app for user-related information. Later, you can expand the application to different ideas like payment, security, advertisement, etc.

Make sure your command prompt is still in the outer app directory. If the development server is still running, use Control + C to terminate it. Type in the following to create an app:

python manage.py startapp restaurants

Your directory structure should look like this:

app
— app
— restaurants

Finally, you need to register restaurants to the Django project. Simply go to app/app/settings.py and add

'restaurants.apps.RestaurantsConfig'

to the INSTALLED_APPS array. The array should now look like this

INSTALLED_APPS = [
'restaurants.apps.RestaurantsConfig',
'accounts.apps.AccountsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

3 steps to create and register models

Step #1, go to the file models.py in folder restaurants and add:

from django.db import modelsclass Restaurant(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=100)
latitude = models.FloatField(default=0)
longitude = models.FloatField(default=0)
def __str__(self):
return self.name

Let’s me explain what the code does. It created a data model to represent our model of a restaurant. We use models.CharField when our fields are of String type and models.FloatField when our fields are of Float type. Of course, there are other different field types that models can offer to suit your needs. The function __str__ allows restaurant objects to be represented by their name in the admin interface. We’ll get to that right away.

Here’s a rule of thumb: Whenever you make a change in models.py (create, update, or delete models), you need to make migration files and execute them. This allows updating the tables in your database.

python manage.py makemigrationspython manage.py migrate

Speaking of databases, at this moment, you are still using SQLite database. This comes by default with Django. However, you can still use other databases if you want. Simply head to this link to see the necessary configuration.

At this step, if you start your development server, you will see that the admin page still does not display information about restaurants. For that, you need to go to file admin.py in directory restaurants and manually register the model.

from django.contrib import admin
from .models import Restaurant
class RestaurantAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
(None, {'fields': ['address']}),
(None, {'fields': ['latitude']}),
(None, {'fields': ['longitude']})
]
list_display = ('name', 'address', 'latitude', 'longitude')
admin.site.register(Restaurant, RestaurantAdmin)

Step #2, refresh your admin page, and you will see a new entry for your restaurants on the column on the left-hand side. Click on that entry to enter the admin site for restaurants.

The purpose of this step is to identify which fields should be tracked and displayed.

Step #3, Input the information of your desired restaurant and click Save.

Create and register models for dishes and tags

However, you want more than just one model. You also want models for dishes and tags.

from django.db import modelsclass Restaurant(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=100)
latitude = models.FloatField(default=0)
longitude = models.FloatField(default=0)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=30, unique=True)
restaurants = models.ManyToManyField(Restaurant)

def __str__(self):
return self.name
class Dish(models.Model):
name = models.CharField(max_length=30)
description = models.CharField(max_length=100)
price = models.FloatField(default=0)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
def __str__(self):
return self.name

For tags, note that one restaurant can have many tags (Italian, sandwiches, …) and one tag can be used for many restaurants. Thus, you can use models.ManyToManyField to represent this relationship.

Important: if you want to create a many-to-many relationship, creating as many fields in one model in the relationship is enough. That means you must not add this line in the class Restaurant.

tags = models.ManyToManyField(Tag)

For dishes, one restaurant has multiple dishes. Therefore, you can use models.ForeignKey for this purpose. The argument on_delete=models.CASCADE tells Django that if a restaurant gets deleted, its dishes should be deleted as well.

Just like before, you need to register an admin site for each of the new models

from django.contrib import admin
from .models import Restaurant, Tag, Dish
class DishInline(admin.TabularInline):
model = Dish
extra = 3
class RestaurantTagInline(admin.TabularInline):
model = Tag.restaurants.through
extra = 3
class RestaurantAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
(None, {'fields': ['address']}),
(None, {'fields': ['latitude']}),
(None, {'fields': ['longitude']})
]
inlines = [DishInline, RestaurantTagInline]
list_display = ('name', 'address', 'latitude', 'longitude')
class TagAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
]

inlines = [RestaurantTagInline]
class DishAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
(None, {'fields': ['description']}),
(None, {'fields': ['price']}),
(None, {'fields': ['restaurant']}),
]
list_display = ('name', 'description', 'price', 'restaurant')admin.site.register(Restaurant, RestaurantAdmin)
admin.site.register(Tag, TagAdmin)
admin.site.register(Dish, DishAdmin)

You can use Inline class to display models in one-to-many or many-to-many relationships.

  • First, you need to define the Inline classes.
  • Then, you need to specify which models the classes should be responsible for. The extra property is for the number of objects that will be displayed in the admin site that uses the Inline class.
  • Finally, classes on the other end of the relationships can use property inlines to specify which Inline classes to use.

CRUD API endpoints

You need to create a file named urls.py in directory restaurants. In that file, copy the following code snippet:

from django.urls import path
from . import views
urlpatterns = [
path('restaurants/new/', views.create_a_new_restaurant, name='create_a_new_restaurant'),
path('restaurants/', views.get_all_restaurants, name='get_all_restaurants'), path('restaurants/<int:restaurant_id>/', views.get_restaurant_by_id, name='get_restaurant_by_id'), path('restaurants/patch/<int:restaurant_id>/', views.update_restaurant_by_id, name='update_restaurant_by_id'), path('restaurants/delete/<int:restaurant_id>/', views.delete_restaurant_by_id, name='delete_restaurant_by_id'),
]

What it does is map URL patterns to handling methods defined in views.py in directory restaurants. The name argument is for reversing the URL, which we’ll go no further in this tutorial. We use <type:parameter_name> to define the parameter that may be appended to the URLs. Next, you need to register the URL patterns of the restaurant app in our Django project. Simply go to urls.py in the directory app in the project directory app. You can see that a URL for the admin site has already been registered. Now you need to do the same thing for the restaurants app.

from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('api/', include('restaurants.urls')),
path('admin/', admin.site.urls),
]

If you want to make an HTTP GET request to get a restaurant with id 1, you can use the URL:

http://127.0.0.1:8000/api/restaurants/1/

You will get an error because this URL is not declared yet. We will go through that in the steps below.

You are ready to implement the handling methods in restaurants/views.py.

Import modules

from django.http import HttpResponse
from django.core import serializers
from django.http import Http404, HttpResponseNotAllowed
from .models import Restaurant, Dish, Tag
import json

Create API endpoint

def create_a_new_restaurant(request):
if request.method == 'POST':
body = json.loads(request.body.decode('utf-8'))
new_name = body.get("name")
new_address = body.get("address")
new_latitude = body.get("latitude")
new_longitude = body.get("longitude")
try:
tag = Tag.objects.get(pk=body.get("tag_id"))
except Tag.DoesNotExist:
raise Http404("Tag does not exist")
new_restaurant = Restaurant(name=new_name, address=new_address, latitude=new_latitude, longitude=new_longitude)
new_restaurant.save()
tag.restaurants.add(new_restaurant)
tag.save()
return HttpResponse(status=200)
else:
raise HttpResponseNotAllowed("Method is not supported")

You need to extract the information from the request, instantiate an instance of Restaurant, and save it. You also need to get an already existing tag (Remember to create one at hand and use its id for your new restaurant) and add that restaurant to the tag’s restaurants.

Read API endpoint

def get_all_restaurants(request):
query_set = Restaurant.objects.all()
data = serializers.serialize("json", query_set)
return HttpResponse(data)
def get_restaurant_by_id(request, restaurant_id):
try:
query_set = Restaurant.objects.filter(pk=restaurant_id)
except Restaurant.DoesNotExist:
raise Http404("Restaurant does not exist")
data = serializers.serialize("json", query_set)
return HttpResponse(data)

For the get-all method, you can query all the objects from the Restaurant model. Then you serialize the objects and return the data. For the get by id method, you can query for the restaurant based on the id given in the URL, serialize it and return the data.

Update API endpoint

def update_restaurant_by_id(request, restaurant_id):
if request.method == 'PUT':
body = json.loads(request.body.decode('utf-8'))
new_name = body.get("name")
new_address = body.get("address")
new_latitude = body.get("latitude")
new_longitude = body.get("longitude")
try:
tag = Tag.objects.get(pk=body.get("tag_id"))
except Tag.DoesNotExist:
raise Http404("Tag does not exist")
try:
restaurant = Restaurant.objects.get(pk=restaurant_id)
except Restaurant.DoesNotExist:
raise Http404("Restaurant does not exist")
restaurant.name = new_name
restaurant.address = new_address
restaurant.latitude = new_latitude
restaurant.longitude = new_longitude
restaurant.save()
tag.restaurants.add(restaurant)
tag.save()
return HttpResponse(status=200) else:
raise HttpResponseNotAllowed("Method is not supported")

Similar to the Read API endpoint, you look for the tag and the restaurant by their ids. Then you change the properties of the found restaurant, found tag, and save them.

Delete API endpoint

def delete_restaurant_by_id(request, restaurant_id):
if request.method == 'DELETE':
try:
restaurant = Restaurant.objects.get(pk=restaurant_id)
except Restaurant.DoesNotExist:
raise Http404("Restaurant does not exist")

restaurant.delete()
return HttpResponse(status=200)
else:
raise HttpResponseNotAllowed("Method is not supported")

You get the restaurant that we want to delete by the id provided in the URL, then simply call the delete() method on that instance.

URLs and handling methods for Dish and Tag follow the same pattern. You can apply what you have learned to implement them.

Conclusion

So far, what you have done is just a fraction of what Django can offer. There are so many aspects that we can consider if you want to expand our application. For example, payment, security, authorization, etc.

If you want to learn more, this link makes great material. I wish you all the best on your journey of backend development!

--

--