Django development tips
Archie ToThis article contains tips that I wish I knew when starting out with Django. Following these tips will help your development process with Django a lot faster and make your code way cleaner. This article assumes you already have basic Django knowledge. (Django’s full documentation can be found here.)
Note: All the code examples below assume there is no bug within each code, all dependencies for each code have been set up and each code runs correctly. The purpose of this tutorial is not to teach you Django code, but to show you the available options that Django offers to developers.
Template inheritance and inclusion
Template inheritance
For example, you are building an app that contains a page title, imports Bootstrap and Jquery and has a navigation bar on every page. The code might look like this:
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Home page</title>
<script src="<jquery link>"></script>
<link href="<bootstrap css link>" rel="stylesheet">
<script src="<bootstrap javascript link>"></script>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<h1>Home page</h1>
</body>
</html>
about.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>About page</title>
<script src="<jquery link>"></script>
<link href="<bootstrap css link>" rel="stylesheet">
<script src="<bootstrap javascript link>"></script>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<h1>About page</h1>
</body>
</html>
As you can see, there is a lot of code repetition in the example above. To prevent this, Django allows developers a way to define a skeleton template, and have other templates inherit this skeleton template. Using the example above, the code can be transformed into the followings:
Create base.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
<script src="<jquery link>"></script>
<link href="<bootstrap css link>" rel="stylesheet">
<script src="<bootstrap javascript link>"></script>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
{% block content %}{% endblock %}
</body>
</html>
index.html
:
{% extends "base.html" %}
{% block title %}Home page{% endblock %}
{% block content %}
<h1>Home page</h1>
{% endblock %}
about.html
:
{% extends "base.html" %}
{% block title %}About page{% endblock %}
{% block content %}
<h1>About page</h1>
{% endblock %}
The resulting index.html
and about.html
has the exact same contents as the initial ones shown above.
Reference: Django template inheritance
Template inclusion
Django also allows developers to include other HTML files in an HTML file. For example, if you are using a form on two pages:
index.html
:
<body>
<h1>Home page</h1>
<form>
<input type="text" name="name" placeholder="Your name">
<input type="number" name="age" placeholder="Your age">
<input type="submit">
</form>
</body>
info.html
:
<body>
<h1>Info page</h1>
<form>
<input type="text" name="name" placeholder="Your name">
<input type="number" name="age" placeholder="Your age">
<input type="submit">
</form>
</body>
You can write a separate template for the form, and use the {% include %}
tag to include the template in both index.html
and info.html
:
Create form.html
:
<form>
<input type="text" name="name" placeholder="Your name">
<input type="number" name="age" placeholder="Your age">
<input type="submit">
</form>
index.html
:
<body>
<h1>Home page</h1>
{% include 'form.html' %}
</body>
info.html
:
<body>
<h1>Info page</h1>
{% include 'form.html' %}
</body>
The resulting index.html
and info.html
has the exact same contents as the initial ones shown above.
Reference: Django template include tag
Write context processor to create global template context variables
For example, you want to create multiple templates that has the same context variable user
, you’d write it as follows:
views.py
:
def home(request):
return render(request, 'index.html', {'user': 'user@example.org'})
def about(request):
return render(request, 'about.html', {'user': 'user@example.org'})
index.html
:
<body>
<h1>Home page</h1>
<h2>{{ user }}</h2>
</body>
about.html
:
<body>
<h1>About page</h1>
<h2>{{ user }}</h2>
</body>
In the above, we have to define the context variable user
twice. This can add up quickly as the number of templates and repeating context variable rises. To avoid this, we can create our own context processor as follows:
settings.py
:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'app_starter.custom_contexts.get_user', # We add this entry, all the aboves are default
],
},
},
]
app_starter/custom_contexts.py
:
def get_user(request):
return {'user': 'user@example.org'}
views.py
:
def home(request):
return render(request, 'index.html')
def about(request):
return render(request, 'about.html')
Note: The file path has to match how we includes it in settings.py
.
With index.html
and about.html
remaining the same, user
will be displayed in both pages.
Reference: Write your own context processor
Django built-in admin page
Django offers developers a built-in admin page. With this admin page, one can create, edit, delete and view model instances with ease for development process. To use the admin page:
Build a Question model in models.py
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
Register Question model in admin.py
:
from django.contrib import admin
from .models import Question
admin.site.register(Question)
Visit localhost:<port>/admin
, you see this page:
We need to create an account. In your terminal, run:
$ python manage.py createsuperuser
Username: admin
Email address: admin@example.com
Password: **********
Password (again): *********
Superuser created successfully.
Now you can log in with that account you just created. After logging in, you should see:
After clicking on Questions, you can view all Question instances that you have created. You can create, edit and delete Question instances using this admin dashboard.
Reference: Introducing the Django Admin
Middleware
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output.
In my case, I use middleware to perform actions that are included in most of
my views, so I don’t have to rewrite the actions for each view. For example,
you want only authenticated users to visit your app. If an user is not
authenticated, they should be redirected to the Login page at /login
.
You might write:
views.py
:
def home(request):
if not request.session.get('user', None):
return redirect('/login')
return render(request, 'index.html', {'user': request.session.get('user', None)})
def about(request):
if not request.session.get('user', None):
return redirect('/login')
return render(request, 'about.html', {'user': request.session.get('user', None)})
def contact(request):
if not request.session.get('user', None):
return redirect('/login')
return render(request, 'contact.html', {'user': request.session.get('user', None)})
def login(request):
request.session['user'] = 'user@example.org'
return render(request, 'login.html', {})
See how you have to repeat 2 lines of code for checking user authentication status for each view. You can solve this by doing the following:
settings.py
:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
"whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.authenticate_middleware.AuthenticateMiddleware', # We added this line, all the aboves are default
]
middleware/authenticate_middleware.py
:
from django.shortcuts import redirect
class AuthenticateMiddleware():
def __init__(self, get_response):
"""This function is required"""
self.get_response = get_response
def __call__(self, request):
"""Actions preformed by this middleware are defined here"""
if request.path != "/login" and not request.session.get('user', None):
return redirect('/login')
response = self.get_response(request)
return response
Note: The file path has to match how we includes it in settings.py
.
views.py
:
def home(request):
return render(request, 'index.html', {'user': request.session.get('user', None)})
def about(request):
return render(request, 'about.html', {'user': request.session.get('user', None)})
def contact(request):
return render(request, 'contact.html', {'user': request.session.get('user', None)})
def login(request):
request.session['user'] = 'user@example.org'
return render(request, 'login.html', {})
Reference: Write your own middleware
Custom Django commands
Django provides a way for you to write your own python manage.py
command. This can come in really handy in the development process. For example, you want to list the names all of the users that have been created for your Django app, you can write a command python manage.py listusers
as follows:
Assuming you have an application called myapp
, you need to created a file myapp/management/commands/listusers.py
. The file path needs to be exactly like that.
listusers.py
:
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
class Command(BaseCommand):
help = 'List the names of all created users, separated by a comma'
# def add_arguments(self, parser):
""" Define arguments that can be received by the command.
We are not using this right now but it's useful to put it here.
"""
# parser.add_argument('user_ids', nargs='+', type=int)
def handle(self, *args, **options):
""" Define what the command will do """
self.stdout.write(", ".join(list(User.objects.all().value_lists('username', flat=True))))
You can test this out by running python manage.py shell
. When a Python shell is opened, run:
>>> from django.contrib.auth.models import User
>>> User.objects.create(username="user@example.org", password="testinguser")
>>> User.objects.create(username="admin@example.org", password="testingadmin")
>>> exit()
Back to your terminal shell, run:
$ python manage.py listusers
user@example.org, admin@example.org
Reference: Create custom django-admin commands