DEV Community

Cover image for Building a Simple Polling App in Django: Step-by-Step Guide

Building a Simple Polling App in Django: Step-by-Step Guide

We will go through setting up a Django project from scratch to create a simple polling application. This guide will cover everything from installing Python and Django to configuring your app with models, views, and templates. Along the way, we will build a feature that lets users vote in a poll, and we will explore key concepts like generic views, models, and URL routing.

Step 1: Installing Python and Setting Up a Virtual Environment

Before you start with Django, make sure you have Python installed on your machine. If you don’t have it installed, you can download Python from python.org.

To keep your project’s dependencies isolated, it’s best to use a virtual environment.

# Install virtualenv if you don't have it
pip install virtualenv

# Create a virtual environment
virtualenv venv

# Activate the virtual environment
venv\Scripts\activate
Enter fullscreen mode Exit fullscreen mode

Step 2: Installing Django and Starting a Project

With your virtual environment activated, install Django:

pip install django
Enter fullscreen mode Exit fullscreen mode

Now, create a new Django project called mysite:

django-admin startproject mysite
Enter fullscreen mode Exit fullscreen mode

This command creates a mysite directory that contains your project files.

Step 3: Creating the Polls App

Django follows a modular approach, and every feature in Django is usually built inside an "app". We will create an app called polls.

cd mysite
python manage.py startapp polls
Enter fullscreen mode Exit fullscreen mode

Next, register the polls app in mysite/settings.py by adding it to the INSTALLED_APPS list:

INSTALLED_APPS = [
    # other apps...
    'polls',
]

Enter fullscreen mode Exit fullscreen mode

Step 4: Defining Models for Questions and Choices

In polls/models.py, we will define two models: Question and Choice. Each Question will have several Choices that users can vote on.

from django.db import models
from django.utils import timezone
import datetime

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

Enter fullscreen mode Exit fullscreen mode

Step 5: Creating and Migrating the Database

We will be using SQLite for our database, which is the default for Django. The configuration can be found in mysite/settings.py under the DATABASES section:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

Enter fullscreen mode Exit fullscreen mode

After setting up the models, you need to generate the migrations and apply them to create the database schema:

python manage.py makemigrations
python manage.py migrate

Enter fullscreen mode Exit fullscreen mode

Step 6: Creating Views for Polls

We will use Django’s generic views to simplify the process of creating list and detail views. In polls/views.py, define views for listing questions, showing details of a question, and displaying results.

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.db.models import F
from django.views import generic
from django.utils import timezone
from .models import Question, Choice

class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        return Question.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]

class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

    def get_queryset(self):
        return Question.objects.filter(pub_date__lte=timezone.now())

class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes = F('votes') + 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

Enter fullscreen mode Exit fullscreen mode

Step 7: Mapping URLs to Views

In polls/urls.py, define the URL patterns for our app, which will map the URL paths to the views we just created.

from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

Enter fullscreen mode Exit fullscreen mode

Then, include the polls/urls.py in the main mysite/urls.py:

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

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

Enter fullscreen mode Exit fullscreen mode

Step 8: Creating Templates

In polls/templates/polls/, create the HTML templates. First, index.html will display the list of questions:

{% load static %}

<link rel="stylesheet" href="{% static 'polls/style.css' %}">

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li> 
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

Next, the detail.html template will display the voting form for a question:

<h1>{{ question.question_text }}</h1>
<form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
    <input type="submit" value="Vote">
</form>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

Enter fullscreen mode Exit fullscreen mode

Lastly, results.html will display the voting results:

<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
Enter fullscreen mode Exit fullscreen mode

Step 9: Running the Development Server

Now that everything is set up, you can start the Django development server:

python manage.py runserver

Enter fullscreen mode Exit fullscreen mode

Top comments (0)