Download Kite Free! Install Kite Free!

Easily Install Django: A Quick Guide

Damian Hites
April 12, 2019

The goal of this article is to get you started using Django to build a web application as quickly as possible, and set you on the road to a production-ready application. For demonstration purposes, we’ll be creating a simple blog for our company called Foo.

Installation

Assuming you have Python setup with pip – installation is simple. This will install Django along with a useful command that will help you get a project started quickly.

pip install django

The following command creates the project directory foo/ and creates a basic project structure and some initial files.

django-admin startproject foo

Let’s quickly go over each file we created:

manage.py is the command-line utility that Django provides. It comes with a number of commands that are supported out of the box, some of which we’ll see later. Some Django applications that you may install later on will add commands accessible through this utility, and ultimately you’ll be able to add your own as needed.

foo/settings.py is an initial example of a Django settings file that should contain all the configuration for your application. If you take a look, you will see the default values that set your application up for running in a local development environment.

foo/urls.py is the default location for your URL configuration. Essentially this provides Django with the rules of how to route requests your application receives.

foo/wsgi.py is where the actual WSGI application lives. When running Django in production, you’ll want to use a server like uwsgi or green unicorn, whose servers interface with the application that resides in this file. Now that we’ve created the project, let’s take it for a spin. To run the built-in development server, run:

./manage.py runserver

You should see some output indicating the settings used and where the server is available. Note that by default, it makes the server available at http://127.0.0.1:8000. If you go to that address, a default page will load, letting you know that you have successfully installed Django. As the page indicates, the only reason you’re seeing that page is because the default configuration provided in the settings.py file has DEBUG = True. Otherwise, trying to reach that page would give you a 404 as your URL configuration in urls.py doesn’t route that path to anything within your application.

Keeping track of dependencies

Before we move on, let’s create a requirements.txt file in the base directory of the project to keep track of any dependencies we install using pip. So far, we’ve only installed Django, so our requirements.txt file will look something like:

django==2.1.7

Creating a Django Application

The terminology gets a bit confusing, but a Django project is made up of a number of applications (not to be confused with the WSGI application). To start working on our blog, let’s create an application that will provide the blog functionality to our Django project:

./manage.py startapp blog

The startapp command, like startproject, creates a template application for you. Once again, let’s take a look at the files we created:

blog/__init__.py is the standard python __init__.py to make the directory into a Python module.

blog/admin.py is the file for registering your models with the Django Admin.

blog/apps.py contains the Django Application configuration object. This tells Django details about your application.

blog/migrations/ is a folder for your database migrations. This is mostly managed by Django’s ORM. We will see how to make migrations and apply them later.

blog/models.py is where you should put your models using the Django ORM. This represents the interface with your backend data store.

blog/tests.py provides a place to put the tests for testing your application.

blog/views.py provides definitions for your applications views. This is where our logic for handling requests go.

Registering Your Application

In order to let your Django project know about your application, you’ll need to register it by adding it to INSTALLED_APPS in settings.py. Your INSTALLED_APPS should now look this:

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

Defining Models

Now that our application is registered, let’s define the data model for our blog. Let’s say we want our blog to show a list of posts that are tagged with various topics and that people who have registered with our blog have the ability to comment on the posts.

We define these models in blog/models.py:

from django.conf import settings
from django.db import models


class Topic(models.Model):
slug = models.SlugField(max_length=50, unique=True)
name = models.CharField(max_length=50)


class Post(models.Model):
slug = models.SlugField(max_length=50, unique=True)
title = models.CharField(max_length=50)
body = models.TextField()
published_on = models.DateTimeField(null=True, blank=True)
topics = models.ManyToManyField('Topic', related_name='posts')


class Comment(models.Model):
post = models.ForeignKey(
'Post', on_delete=models.CASCADE, related_name='comments'
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='comments'
)
body = models.TextField()
commented_on = models.DateTimeField(auto_now_add=True)

There are a lot of details here, but basically we have defined the objects described above, as well as the relationship between these objects and Django’s user model used in authentication. With our models defined, we want to generate our database migrations so that we can build our database tables.

To generate migrations, we can use Django’s makemigrations command:

./manage.py makemigrations

When you run this command, you’ll see a summary of the changes made and the name of the migrations files generated. In this case, it should only be one file and will be the initial migration for our blog application. If you open blog/migrations/0001_initial.py, you’ll see what steps Django has determined are required to change the current database state into the newly defined state. To apply these changes, we use the migrate command:

./manage.py migrate

Since this is the first time we’re running the migrate command, a number of other migrations will also run. These migrations come from some of Django’s built-in applications which include support for:

  • Authentication User model and permissions
  • Admin tracking models
  • ContentTypes framework
  • Sessions

If you look at your project’s base directory, you’ll notice a new file: db.sqlite3. The default configuration for Django is to use SQLite3 as the backend data store which writes a database file into the current working directory.

Managing Your Data

Now that we’ve defined our data model and created our database tables, we’d like a way to manage the data in our database. Django’s admin is an incredibly fast way to generate an interface for managing the data stored in your models. In blog/admin.py, we can quickly register our models to make a management interface available in the Django admin:

from django.contrib import admin

from .models import Comment, Post, Topic


admin.site.register(Comment)
admin.site.register(Post)
admin.site.register(Topic)

Now with our models registered, we can go to the Django admin, which by default (looking at foo/urls.py) is hosted at /admin, which on our local server can be accessed at http://localhost:8000/admin/. When you try to access the admin, however, you are redirected to a login screen. We don’t have a user created yet in our system so we are unable to login, so let’s create a new user with admin access.

Once again, Django has a command for this:

./manage.py createsuperuser

You’ll be prompted for a username, email and password (twice). Once you have created a new superuser (admin), you can login and explore the interface. You’ll see that without having to change anything, there is a section on Authentication and Authorization which can be used to manage Django’s authentication and permissions system that has Users and Groups. You’ll then find a section for our blog application which supports an interface for creating, editing and deleting topics, posts and comments.

Presenting Your Posts

Now that we can create and store the data, we want to create a page that provides a listing of all our posts and a page that displays a single post allowing users to comment. Each page requires us to write a view (for handling the request), a template (for rendering the html) and a url configuration (for routing the request).

Views

Let’s start off by defining the two views in blog/views.py:

from django.shortcuts import redirect, render, reverse
from django.views.generic import DetailView, ListView

from .forms import CommentForm
from .models import Post


class PostListView(ListView):
model = Post


class PostDetailView(DetailView):
model = Post

def post(self, request, slug=None):
form = CommentForm(data=request.POST)
if form.is_valid():
form.save()
return redirect(
reverse('blog:post_detail', kwargs={'slug': slug})
)
return self.get(request, slug=slug)

def get_context_data(self, **kwargs):
ctx = super(PostDetailView, self).get_context_data(**kwargs)
ctx['form'] = CommentForm(
initial={
'post': self.object,
'user': self.request.user
}
)
return ctx

Django provides some generic views that handle a lot of the logic we need. The ListView allows you to specify a model and retrieves a list of all the objects in that table for use in your template. The DetailView allows you to specify a model and will retrieve a single object from the table, given a unique identifier in your url configuration. In this case we’ll be using the slug defined on the Post model.

Since we also want to support commenting, we override the get_context_data method to add the CommentForm defined in blogs/forms.py to the context that the DetailView will pass to the templating engine for rendering the template. We also need to add a post method to the DetailView that uses the CommentForm to create comments:

from django import forms

from .models import Comment


class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('post', 'user', 'body')
widgets = {
'post': forms.HiddenInput(),
'user': forms.HiddenInput()
}

Templates

In order to actually render the HTML, we want to use templates. By default, the built-in Django views will look for the Post list template at blog/templates/blog/post_list.html and for the Post detail template at blog/templates/blog/post_detail.html. To avoid code duplication, we will create a base template and extend that template to create the templates for our two pages.

First, enter blog/templates/blog/base.html:

<!DOCTYPE html>
<html>
  <head>
    <title>{% block title %}Foo Blog{% endblock title %}</title>
  </head>
  </head>
  <body>
    {% block content %}
    {% endblock content %}
  </body>
</html>

Now we can create the HTML for the list view in blog/templates/blog/post_list.html:

{% extends "blog/base.html" %}

{% block content %}
<h1>Foo Blog</h1>
<div>
  {% for post in object_list %}
    <div>==========================================================</div>
    <div>Posted on {{ post.published_on }}</div>
    <div><h3>{{ post.title }}</h3></div>
    <div>{{ post.snippet }}...<a href="{% url "blog:post_detail" slug=post.slug %}">continue reading.</a></div>
    <div>==========================================================</div>
    <br/>
  {% endfor %}
</div>
{% endblock content %}

And the HTML for the detail view in blog/templates/blog/post_detail.html is:

{% extends "blog/base.html" %}

{% block content %}
<h3>{{ object.title }}</h3>
<h4>Topics</h4>
<ul>
  {% for topic in object.topics.all %}
    <li>{{ topic.name }}</li>
  {% endfor %}
</ul>
<div>Posted on {{ object.published_on }}</div>
<br/>
<div>{{ object.body }}</div>
<br/>
<div>
  <h4>Comments</h4>
  {% for comment in object.comments.all %}
    <div>---------------------------------------------------</div>
    <div>{{ comment.body }}</div>
    <div>Commented on {{ comment.commented_on }}</div>
    <div>---------------------------------------------------</div>
  {% endfor %}
</div>
{% if user.is_authenticated %}
<div>
  <h4>Leave a Comment</h4>
  <form method="post">
    {% csrf_token %}
    <div>{{ form.post }}</div>
    <div>{{ form.user }}</div>
    <div>{{ form.body }}</div>
    <button>Submit</button>
  </form>
</div>
{% endif %}
<br/>
<div><a href="{% url "blog:post_list" %}"><< Back to Post List</a></div>
{% endblock content %}

URL Configuration

Finally, we want to route requests to our views to make them available in our web application. Since we may want this blog to be just a single part of our project (and web application), we can keep things nicely separated and modular by defining the blog’s url configuration within the blog application and including that configuration in the project’s url configuration. Let’s start by created blog/urls.py:

from django.urls import path

from .views import PostDetailView, PostListView

app_name = 'blog'

urlpatterns = [
path('', PostListView.as_view(), name='post_list'),
path('/', PostDetailView.as_view(), name='post_detail'),
]

Then, modify foo/urls.py:

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

urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace='blog')),
]

Now our blog is available at http://localhost:8000/blog/ and each post will be accessible at http://localhost:8000/blog/<slug >/.

Summary

With minimal steps and time spent, we have implemented a rudimentary blog and while it has no styling, it is fully functional. Taking advantage of Django’s built-in admin interface, we can manage all our data: topics, posts and comments. The data gets persisted through our model definitions using Django’s ORM. Finally, we set up some routes, views and templates to make our posts publicly accessible on our web application.

What Next?

The blog application that we have created is functional, but basic. Ideally we would want to add some design/styling to our templates to present our posts in a nicer way. We might also want to have an editor in our admin tool with the ability to apply formatting that would translate to html for displaying the post. We may want the ability to filter by topics or search for specific posts. Each of these additions is easily doable, either through a 3rd party extension to Django or within the framework. Finally, the blog will need to be deployed in a production environment somewhere which will require support for production settings in the application, a hosting solution, deployment mechanisms, production web servers and application servers to host your application and a virtual or physical server to host all of this software. These topics are out of scope of this article, but there are many resources available on the Kite blog to give you ideas and help with how to get this stuff setup.