Getting Started with Django

Apr 23, 2019

Programming Tutorial

By: Brandon Quakkelaar

Django is a Python framework that I’ve written about before. I’ve compared Django to ASP.NET MVC. But, I didn’t go into much detail about how to actually use Django to create a data driven website. That’s what I’ll cover now.

The goal for this ‘Getting Started’ project is to get familiar with the basics of developing data driven web applications using Django, Models, Views, Templates, URLs, and Forms. By the end we’ll have a simple contact form that saves user submissions to the database, and displays that data to an administrator user. It will not be a completed site, but it will introduce basic the building blocks of website development in Django.

If you’d like to refer to the end goal of this article, you can find it on GitHub.

Prerequisites

Create Project

Make the project directory and navigate inside.

> mkdir django-project && cd django-project

Create a Virtual Environment to isolate project specific dependencies.

> python -m venv django-env

Activate the venv. (This is the Windows command.)

> django-env\Scripts\activate.bat

Note: Once inside an activated venv, you can leave it to work on other things by using the deactivate command.

Install Django inside the active venv. (You don’t want to install packages globally. Make sure that the venv is active.)

(django-env)> pip install django

Use django-admin to create the new Django application.

(django-env)> django-admin startproject djangosite

That created a new folder called djangosite and it placed boilerplate files inside. manage.py is one of those files and it’s what will used from now on to run Django commands.

Run the development Django server from inside the site’s folder.

(django-env)> cd djangosite
(django-env)> python manage.py runserver

Visit localhost:8000 to verify that it’s working. Then exit the server with Ctrl+Break.

When the Django server ran, Django automatically created a db.sqlite3 file in the project folder. Read more about configuring different database backends.

Use VS Code to open the django-project folder that houses the virtual environment folder and the Django app folder. This can be done from the terminal, or manually inside VS Code.

> code ..\. 

Open djangosite\urls.py. The imports for Django will probably be shown as missing modules because Django was installed in the venv and not globally. to fix this, press ctrl+shift+p then execute the command Python: Select Interpretter. Find .\django-env\Scripts\python.exe from the list and select it. Then VS Code will stop showing the Django imports as errors.

For more VS Code information visit Getting Started with Python in VS Code.

Add a Custom Django View

To add a ‘Hello World’ page, create a new file in django-project\djangosite\djangosite called views.py. Add the following code.

from django.http import HttpResponse

def welcome(request):
  return HttpResponse("Hello, World!")

To tell Django when to execute this welcome view, edit urls.py so it looks like this:

from django.contrib import admin
from django.urls import path, re_path

from .views import welcome

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^$', welcome)
]

Browse to localhost:8000 and see the custom message.

Django Hello World

Let’s take a quick break to go over some Django concepts.

App

A Django App is a python package that acts as a little web application of its own. With it’s own Models, Views, Templates, and URLs. Most Django sites use several apps. It is also possible to write an app such that it can be reused in other Django projects.

Model

A Django Model is a data model that maps to a database table. This introduces the concept of Migrations.

Migrations

The model will usually change as the site is developed. A migration will update the corresponding database table from previous versions to the new version of the code. As migrations get more complicated, there may be need to write custom migration scripts.

Views

Views in Django are like Controllers in ASP.NET MVC. They are what connect a model with a template.

Templates

Templates in Django are like Views in ASP.NET MVC. They are the display layer; the HTML that will render in the browser.

Add a Custom Django Model

Show migrations

(django-env)> python manage.py showmigrations

Execute those migrations

(django-env)> python manage.py migrate

Now create a new Django app. For this example, it will handle the functionality for a contact form.

(django-env)> python manage.py startapp contactform

Move into the new contactform directory and notice that Django has created a bunch of boilerplate code for us.

(django-env)> cd contactform
(django-env)> dir

Add this new app to the Django site. Open settings.py in the djangosite folder, and add the contactform app to the list of installed apps. INSTALLED_APPS should look like this:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'contactform'
]

Then, open the models.py file in the contact form app and add the class that will represent the contact form.

from django.db import models

class ContactForm(models.Model):
    HOW_DID_YOU_HEAR_ABOUT_US_CHOICES = (
        ('SE', 'Search Engine'),
        ('Q', 'Quakkels.com'),
        ('F', 'From a friend'),
        ('O', 'Other')
    )

    name = models.CharField(max_length=50)
    email = models.EmailField(max_length=50)
    comments = models.CharField(max_length=500)
    how_did_you_hear_about_us = models.CharField(max_length=2, choices=HOW_DID_YOU_HEAR_ABOUT_US_CHOICES)

More information about Django models can be found here.

After the model is saved, create the new migration script.

(django-env)> python manage.py makemigrations

Run the migration.

(django-env)> python manage.py migrate

If you’re curious about what the SQLite database tables look like, you can download something like DB Browser for SQLite. Table names will be named after the model with the app name as the prefix. So the table for ContactForm will be called contactform_contactform.

Register Custom Model in Admin

To make contact form submissions available to be read by a site administrator, ContactForm needs to be registered in the admin area.

Open contactform\admin.py and add this code to register the ContactForm model.

from django.contrib import admin
from .models import ContactForm

@admin.register(ContactForm)
class ContactFormAdmin(admin.ModelAdmin):
    list_display = ('name', 'email')

Create a super user for logging into the site’s admin area.

(django-env)> python manage.py createsuperuser

Now when the development server runs, you’ll be able to view and edit ContactForms in the Admin area by navigating to localhost:8000/admin and using the new super user to log in.

Create a Custom Django Template

Double check the terminal’s working directory is the contactform folder inside of djangosite.

(django-env)> pwd

Switch to contactform if it’s not the working directory.

Create a new folder for custom templates.

(django-env)> mdkir templates

In VS Code, create a file in the new templates folder called contactform.html for the contact form. Put the following code in the file for now.

<!DOCTYPE html>
<html>
    <head>
      <title>Contact Form</title>
    </head>
    <body>
        <h1>Contact Form</h1>
    </body>
</html>

Next, update contactform\views.py with this code.

from django.shortcuts import render

def contactform(request):
    return render(request, "contactform.html")

Notice that the render() function is used here instead of returning HttpResponse() directly like the “Hello, World!” view does. The render() function will use the template for the html.

To make the template visible from the browser, we need to route requests to the view based on the URL.

Use VS Code to create a urls.py file inside the contactform folder. Put this code inside it.

from django.urls import re_path

from .views import contactform

urlpatterns = [
    re_path('contactform$', contactform)
]

The re_path() method uses a string as a regular expression. The 'contactform$' ensures that it only matches on paths that end in contactform.

Since djangosite\urls.py is what Django uses to route urls, it needs to be aware of the new contactform\urls.py. Update it with this code.

from django.contrib import admin
from django.urls import path, re_path, include

from .views import welcome

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^contactform/', include('contactform.urls')),
    path('', welcome),
]

'^contactform/' is a regular expression that makes sure matching paths begin with contactform. This is a prefix for everything in the contactform app. So the complete matching path will be localhost:8000/contactform/contactform. For more information read the Django documentation.

Create a Form

Create a new file in contactform called forms.py and put this code in it.

from django.forms import ModelForm
from .models import ContactForm

class ContactFormForm(ModelForm):
    class Meta:
        model = ContactForm
        fields = ('name', 'email', 'comments', 'how_did_you_hear_about_us')

It was at this time that I started regretting how the ContactForm model was named. To stick with Django conventions, this form class will be the name of the model with “Form” appended to it. Hence; ContactFormForm. Future me will not make this mistake.

Update contactform\views.py so it will send the new form class as an object to the template.

from django.shortcuts import render
from django.http import HttpResponse
from .forms import ContactFormForm
from .models import ContactForm

def contactform(request):
    form = ContactFormForm()
    context = { 
        'form' : form,
    }

    return render(request, "contactform.html", context)

Update the contactform.html template to use the form object in the context.

<!DOCTYPE html>
<html>
    <head>
      <title>Contact Form</title>
    </head>
    <body>
        <h1>Contact Form</h1>
        <form method="POST">
            {% csrf_token %} <!--this token is required in order to prevent CSRF attacks-->
            {{ form }} <!-- the form object will render our FormModel -->
            <button type="submit">Send</button>
        </form>
    </body>
</html>

You can find more details about how to properly use templates here. The code above has much room for improvement.

Two things worth noting here are {% csrf_token %} and {{ form }}. The code for CSRF Token prevents this page from being vulnerable to cross site request forgery attacks. The form object is the ContactFrom model converted to HTML form elements via the ModelForm class.

Update the view to accept and save a Contact Form submission.

from django.shortcuts import render, redirect
from django.http import HttpResponse
from .forms import ContactFormForm
from .models import ContactForm

def contactform(request):
    form = ContactFormForm()
    context = { "form" : form }

    if request.method != "POST":
        return render(request, "contactform.html", context)
    
    form = ContactFormForm(data=request.POST)

    if not form.is_valid():
        context["form"] = form
        return render(request, "contactform.html", context)
    
    form.save()
    return redirect("/")

This will render the form on first request. When the form is submitted it will check the values against what the model expects. If the values aren’t valid it will render the form again (ideally to inform the user there were problems and give them a chance to correct mistakes.) If the values are valid, then it will save a new record in the database which an admin will be able to see in Django’s administration section. Once the record is saved, the user gets redirected to the site root.

Summary

There’s a lot more to Django, but this is a fair amount of information to get started with it. What we wrote has an authenticated admin area, a ‘hello’ home page, and a data driven form that persists data to a database. This isn’t a completed web application, but we did cover a lot of essential building blocks.

Resources

Thank you for reading.
Please share this post with a friend, and subscribe to get notified of new posts.
Comments may be sent to blog@quakkels.com.