An Introduction to Django – Part 2

As mentioned in Part 1 of this introductory series, in this part we will look at the Django’s built in admin tool, write more meaningful views, and see how to handle errors.

What is the Admin Tool?

The admin tool allows specified users to add, change, and delete content.  It isn’t intended to be seen or used by the end client (the website visitors), but instead by the site managers.

Create an Admin User

Our first step will be to create an admin user.  Navigate to the location of your file from Part 1 and use the command:

python createsuperuser

Here, the command createsuperuser will start the process for creating a super user (as the name implies).  You should be prompted to enter a username.  We want this to be an admin account so type admin and press enter.

Next, you’ll be prompted for an email address.  Type your desired email address and press enter.

Finally, you’ll be asked to set a password.  Enter this password, press enter, then enter it again.

Start the Development Server

Start up the development server again by using the command:

python runserver

and navigate to

It should look like this:


Log in to the site using the username admin and the password that you just set.  You should see the admin index page, which looks like this:


Make Your App Modifiable in the Admin Tool

In Part 1, we built a simple polling app.  We need to tell the admin tool that the Questions objects should have an interface within the admin tool.

To do this, edit the polls/ file so that it looks like this:

from django.contrib import admin

# Register your models here.
from .models import Question

Reload the admin tool in your internet browser.  It should now look like this:


Modify The App in the Admin Tool

Click on “Questions”.  This new page shows all of the questions in the database (we only have one: “What’s up”) and lets you change it.


To edit the “What’s up?” question, simply click on it.


The Change page was automatically generated based on our Question model.  This means that the input fields (the text, date, and time boxes) were generated to correspond to the field types that we specified in the model (CharField and DateTimeField).  If you’ve built your own GUI, you’ll appreciate this automation.  It means that when you define a Field Type, there will automatically be at least a base level check to make sure the inputs are of the right type.  

You have four options on the bottom, which are self-explanatory: Save, Save and continue editing, Save and add another, and Delete.



We briefly went over views in Part 1 (as well as in the ASP.NET post).  They are pretty important for web development, so it is important to get a better grasp of them.

First, think of a specific view as a kind of web page that follows some specific template and serves a specific function.  The Django tutorial, which I am following for this tutorial, gives the follow examples of views that you might have if your application is a blog:

  • Blog homepage – displays the most recent entries.
  • Entry “detail” page – permalink page for a single entry.
  • Year-based archive page – displays all months with entries in a given year
  • Month-based archive page – displays all days with entries in the given month
  • Day-based archive page – displays all entries in a given day
  • Comment action – handles posting comments to a given entry

Our application is much more simple than a blog.  The views for our poll will be:

  • Question “index” page – display the latest questions
  • Question “detail” page – display a question text, with no results but with a form to vote
  • Question “results” page – display the results for a specific question
  • Vote action – handles voting for a particular choice in a particular question

Our web pages will be delivered by views.  The views will be Python functions (or methods).  Django will determine the view by examining the URL.

Create Our Views

To create these views, open the polls/ file.  Add the following functions:

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

These functions are a little different than our initial index view because they take an additional argument, question_id.  The %s syntax in the HttpResponse string is a string place holder.  It will be replaced by whatever is after the % sign after the string (in this case, question_id).

Next, we need to link these views to urls.  Open the polls/ file and add the following to the urlpatterns list:

urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$',, name='vote'),

This syntax might look crazy.  It is known as regex or regular expressions.  This is a big topic and one that I need to learn more about, so I intend to do a post specifically on that later.  For now, understand that the first input to the url function is defining how the URL must be structured, the second part defines which view to use, and the third gives it a name.

Check to make sure this worked.  Navigate to and you should see ‘You’re looking at question 34’.  Try looking at question 34’s result page by going to,

Write Views that Do Things

Views must return an HttpResponse object that contains the content for the page or raise an error (like Http404).  Beyond that, the views can do whatever you specify.

For instance, the view might read data from your database.  It could generate a PDF, output XML, create a ZIP file, etc.

Lets practice using Django’s own API by editing the index view to display the 5 latest poll questions:

from .models import Question
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

The page’s design comes from the view.  This means that if we want to change the design, we’ll have to change the code in the view.  Lets separate the design from the view by creating a template.

Create a directory called templates in the polls folder.  Inside this new directory, add another directory called polls with a file index.html (so, in total, this is: polls/templates/polls/index.html).

We need to create the polls subdirectory within the templates directory because Django will automatically choose the first template whose name matches.  That means if we have multiple templates in the templates folder (without the subdirectories), it would be unable to distinguish them.  The easiest and best way to ensure that Django gets the right template is by putting the templates inside another directory named for the application itself, like we did above.

Within the index.html file, add this:

{% if latest_question_list %}
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ }}/">{{ question.question_text }}</a></li>
    {% endfor %}
{% else %}
    <p>No polls are available.</p>
{% endif %}

This is HTML code that creates a list of links for each question.

Now we need to update our index view (polls/ to include this template.  Add the following:

from django.http import HttpResponse
from django.template import loader

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    return HttpResponse(template.render(context, request))

This will load the template file from polls/index.html and pass the context (the latest question).  Now, load up the page by navigating to and you should see a bulleted list containing the “What’s up” question.  When you click on it, you automatically go to the page that says “You’re looking at question 1”.

The render() Shortcut

We’ll often be using the return HttpResponse(template.render(context, request)) line, so Django provides a shortcut.

Change the index view to look like this:

from djagno.http import HttpReponse
from django.shortcuts import render
from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

Because of the render shortcut, we now don’t need to import the loader and HttpResponse modules (though we’ll keep the HttpResponse module imported since the detail, results, and voting methods rely on it).

Raise a 404 Error

What if a person requests a page of a question whose ID does not exist?  We’ll want to throw a 404 error.  To do this, edit the detail view (polls/ to look like this:

from django.http import Http404
from django.shortcuts import render

def detail(request, question_id):
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

Here, we try getting the Question object.  If it does not exist, we raise a 404 Error with the text “Question does not exist”.

The get_object_or_404() Shortcut

Again, it’s common to raise a 404 error so Django comes with a shortcut to make this a little easier.  We can rewrite the above like this:

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

Create a Detail Template

Create a file detail.html in the polls/templates/polls/ directory and add the following lines to it:

<h1>{{ question.question_text }}</h1>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}

This will list the questions in large text with the choices in bullets underneath the question name.  Test this out by going to

Remove Hardcoded URLs in Templates

Stepping back a little bit, recall that in our polls/index.html file we hard coded the link to the question using the line:

<li><a href="/polls/{{ }}/">{{ question.question_text }}</a></li>

This isn’t good because if we want to change URLS on a project with a lot of templates, it might get difficult.

To fix this, recall that we named the URLS in the polls/ file.  We can reference these URL names in links like this:

<li><a href="{% url 'detail' %}">{{ question.question_text }}</a></li>

Now, if you want to change the URL of the poll details to something else, instead of changing it in all of the templates, you could just change it in the polls/ file.  This is essentially functionizing your URLs and it is good practice.

Namespacing URL Names

namespace is a set of symbols that organize objects so that they can be referenced by a name.  A namespace you’re really familiar with is your file system.  File systems assign names to files.

Right now, we have just one app (polls).  What if we had a project that had many apps?  Maybe more than one app has a detail view like our polls app.  How would Django know which detail URL to reference when using the {% url %} template tag?

To fix this, lets edit our polls/ file like this:

from django.conf.urls import url

from . import views

app_name = 'polls'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    url(r'^(?P<question_id>[0-9]+)/vote/$',, name='vote'),

Notice that before the urlpatterns list we added a variable app_name with the value ‘polls’.  Now, we can go to the polls/index.html template and change the link to this:

<li><a href="{% url 'polls:detail' %}">{{ question.question_text }}</a></li>

All we did is point to our namespace detail view by changing the string after url to ‘polls:detail’ from just ‘detail’.


That’s it for this tutorial.  Next time, we’ll look at creating a simple form and making views more generic.

Django is starting to click with me a little bit more after this tutorial.  I am still struggling more with this than I have with other tech topics I’ve learned recently.  A major hurdle for me right now is troubleshooting.  If I type something in one file, it is difficult for me to find the error — even which file the error is in.  It also seems like so many files are connected (when you edit A you might need to edit B and C) and these relationships aren’t exactly clear for me yet.  That said, I can feel myself grasping more of this as we go along and I’m excited to be able to build my first real web app (without just copying and pasting code from a tutorial).

Have questions or suggestions?  Please feel free to comment below or contact me.

Leave a Reply

Your email address will not be published. Required fields are marked *