Voting System Project Using Django Framework

Last Updated : 3 Oct, 2026

Django is a Python web framework that provides built-in tools for developing database-driven web applications. In this project, we will build a simple voting system where users can view poll questions, select an option, submit their vote, and see the updated results.

1. Creating Project Directory

  • Create a new directory and move into it.
  • You can also create the directory manually and open it in your code editor.

mkdir pollser_project

cd pollser_project

2. Creating Virtual Environment

  • Creating an isolated Python environment for the project.

python -m venv venv

  • Activate it on Windows:

venv\Scripts\activate

  • For Linux/macOS:

source venv/bin/activate

3. Installing Django

  • Install Django Using pip.

python -m pip install django

  • Verify Installation:

python -m django --version

  • Installed django version will be displayed in the terminal.

4. Creating Django Project

  • Create the project in the current directory.

django-admin startproject pollster .

5. Running Development Server

  • Check that the project was created correctly.

python manage.py runserver

  • Open following address:

http://127.0.0.1:8000/

Screenshot_2026-09-20_14-19-23
  • If default Django welcome page appears, means it worked successfully.

6. Registering the Application

  • Open pollster/settings.py.
  • Add polls to INSTALLED_APPS:
Python
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",

    "polls",
]
  • Without this entry, Django will not load the application as part of the project.

8. Creating Database Models

  • Application needs to store a question, its available options, and the number of votes received by each option.
  • Open polls/models.py.
  • Replace the existing code with this:
Python
from django.db import models


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

    def __str__(self):
        return self.question_text


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

Explanation:

  • Question stores the poll text and publication date.
  • Choice stores each available option.
  • ForeignKey connects a choice to its question.
  • Votes keeps the current count.
  • str() makes records easier to identify in Admin.

9. Creating and Applying Migrations

  • Generate a migration from the models:

python manage.py makemigrations

Screenshot_2026-09-20_14-35-01
  • Apply to the database:

python manage.py migrate

Screenshot_2026-09-20_14-35-28
  • Django will create the required tables.
  • Project uses SQLite by default, so no separate database server is required.

10. Configuring Django Admin

  • Django Admin will be used to create and manage polls.
  • Open polls/admin.py.
  • Replace the existing code with:
Python
from django.contrib import admin

from .models import Choice, Question


class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3


@admin.register(Question)
class QuestionAdmin(admin.ModelAdmin):
    list_display = ("question_text", "pub_date")
    ordering = ("-pub_date",)
    inlines = [ChoiceInline]


admin.site.site_header = "Pollster Admin"
admin.site.site_title = "Pollster Admin"
admin.site.index_title = "Pollster Administration"

11. Creating a Superuser

  • Create an administrative account:

python manage.py createsuperuser

  • Enter the requested username, email address and password.
  • Start the server:

python manage.py runserver

  • Open the admin login page directly:

http://127.0.0.1:8000/admin/

Screenshot_2026-09-20_14-43-21
  • Default Django welcome page at the root URL does not contain an Admin login button. The admin interface has its own URL.

12. Adding a Question

After Logging into Django Admin:

  1. Open Questions.
  2. Click Add Question.
  3. Enter a Question
  4. Add the available choices.
  5. Save the record.

Example:

Screenshot_2026-09-20_14-50-05

13. Creating Application URLs

  • Creating polls/urls.py
  • Add this code in the file:
Python
from django.urls import path

from . import views


app_name = "polls"


urlpatterns = [
    path("", views.index, name="index"),
    path("/", views.detail, name="detail"),
    path(
        "/results/",
        views.results,
        name="results",
    ),
    path(
        "/vote/",
        views.vote,
        name="vote",
    ),
]
  • URL namespace polls allows templates to refer to these routes using names such as polls:index and polls:detail.

14. Connecting the Application URLs

  • Open pollster/urls.py
  • Replace the code content with:
Python
from django.contrib import admin
from django.urls import include, path


urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]
  • URL is now available at:

http://127.0.0.1:8000/polls/

15. Creating the Template Directory

  • Inside pols, create template/polls.
  • There create base.html, index.html, details.html, results.html.
Screenshot_2026-09-20_17-59-06

16. Creating the Base Template

  • Open polls/templates/polls/base.html
  • Add this to the file:
HTML

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>
        Pollster
        {% block title %}{% endblock %}
    title>

    <style>
        * {
            box-sizing: border-box;
        }

        body {
            margin: 0;
            font-family: Arial, sans-serif;
            background: #f4f6f8;
            color: #222;
        }

        nav {
            background: #1f2937;
            padding: 15px 20px;
        }

        nav a {
            color: white;
            text-decoration: none;
            font-size: 20px;
            font-weight: bold;
        }

        .container {
            width: 90%;
            max-width: 800px;
            margin: 40px auto;
        }

        .card {
            background: white;
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
        }

        .button {
            display: inline-block;
            padding: 10px 16px;
            margin-top: 10px;
            border: none;
            border-radius: 5px;
            background: #2563eb;
            color: white;
            text-decoration: none;
            cursor: pointer;
        }

        .button:hover {
            background: #1d4ed8;
        }

        .secondary {
            background: #4b5563;
        }

        .secondary:hover {
            background: #374151;
        }

        .choice {
            margin: 15px 0;
        }

        .error {
            padding: 12px;
            border-radius: 5px;
            background: #fee2e2;
            color: #991b1b;
        }

        ul {
            padding-left: 20px;
        }

        li {
            margin-bottom: 12px;
        }
    style>
head>

<body>

    <nav>
        <a href="{% url 'polls:index' %}">
            Pollster
        a>
    nav>

    <main class="container">

        {% block content %}
        {% endblock content %}

    main>

body>
html>
  • Contains the common page layout.
  • Other templates will inherit it instead of duplicating the same HTML structure.

17. Creating Poll List Page

  • Open polls/templates/polls/index.html
  • Add this to the file:
HTML
{% extends "polls/base.html" %}

{% block title %}
    - Polls
{% endblock %}

{% block content %}

<h1>Poll Questionsh1>

{% if latest_question_list %}

    {% for question in latest_question_list %}

        <div class="card">

            <h2>{{ question.question_text }}h2>

            <p>
                Published:
                {{ question.pub_date }}
            p>

            <a
                href="{% url 'polls:detail' question.id %}"
                class="button"
            >
                Vote Now
            a>

            <a
                href="{% url 'polls:results' question.id %}"
                class="button secondary"
            >
                Results
            a>

        div>

    {% endfor %}

{% else %}

    <div class="card">
        <p>No polls are available.p>
    div>

{% endif %}

{% endblock %}
  • Page receives the latest questions from the index view and creates links for voting and viewing resulting.

18. Creating Voting Page

  • Open polls/templates/polls/detail.html
  • Add this to the file:
HTML
{% extends "polls/base.html" %}

{% block title %}
    - Vote
{% endblock %}

{% block content %}

<a
    href="{% url 'polls:index' %}"
    class="button secondary"
>
    Back to Polls
a>

<div class="card">

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

    {% if error_message %}
        <p class="error">
            {{ error_message }}
        p>
    {% endif %}

    <form
        action="{% url 'polls:vote' question.id %}"
        method="post"
    >

        {% csrf_token %}

        {% for choice in question.choice_set.all %}

            <div class="choice">

                <input
                    type="radio"
                    name="choice"
                    id="choice{{ forloop.counter }}"
                    value="{{ choice.id }}"
                    required
                >

                <label for="choice{{ forloop.counter }}">
                    {{ choice.choice_text }}
                label>

            div>

        {% empty %}

            <p>No choices are available for this question.p>

        {% endfor %}

        <button
            type="submit"
            class="button"
        >
            Vote
        button>

    form>

div>

{% endblock %}
  • Form uses post because submitting a vote changes stored data.
  • CSRF token protects the request, while required on the radio input prevents an empty submission in the browser.

19. Creating Results Page

  • Open polls/templates/polls/results.html
  • Add this to the file:
HTML
{% extends "polls/base.html" %}

{% block title %}
    - Results
{% endblock %}

{% block content %}

<div class="card">

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

    <h2>Voting Resultsh2>

    <ul>

        {% for choice in question.choice_set.all %}

            <li>
                {{ choice.choice_text }}:
                {{ choice.votes }}
                vote{{ choice.votes|pluralize }}
            li>

        {% empty %}

            <li>No choices are available for this question.li>

        {% endfor %}

    ul>

    <a
        href="{% url 'polls:index' %}"
        class="button secondary"
    >
        Back to Polls
    a>

    <a
        href="{% url 'polls:detail' question.id %}"
        class="button"
    >
        Vote Again
    a>

div>

{% endblock %}
  • Each option is displayed together with its current count.

20. Creating Views

  • Open polls/views.py
  • Replace the existing code with:
Python
from django.http import HttpResponseNotAllowed, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]

    return render(
        request,
        "polls/index.html",
        {
            "latest_question_list": latest_question_list,
        },
    )


def detail(request, question_id):
    question = get_object_or_404(
        Question,
        pk=question_id,
    )

    return render(
        request,
        "polls/detail.html",
        {
            "question": question,
        },
    )


def results(request, question_id):
    question = get_object_or_404(
        Question,
        pk=question_id,
    )

    return render(
        request,
        "polls/results.html",
        {
            "question": question,
        },
    )


def vote(request, question_id):
    if request.method != "POST":
        return HttpResponseNotAllowed(["POST"])

    question = get_object_or_404(
        Question,
        pk=question_id,
    )

    choice_id = request.POST.get("choice")

    if not choice_id:
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "Please select a choice.",
            },
        )

    try:
        selected_choice = question.choice_set.get(
            pk=choice_id,
        )

    except Choice.DoesNotExist:
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "The selected choice is invalid.",
            },
        )

    selected_choice.votes += 1
    selected_choice.save()

    return HttpResponseRedirect(
        reverse(
            "polls:results",
            args=(question.id,),
        )
    )
  • It provides the small amount of application logic needed for the project.
  • Index retrieves recent questions.
  • detail loads a selected poll.
  • results retrieves data for the result page.
  • vote validates the request, updates the selected choice, and redirects.

21. Running Application

  • Start the server:

python manage.py runserver

  • Open:

http://127.0.0.1:8000/polls/

Screenshot_2026-09-20_18-20-13

22. Testing Complete Voting Flow

Follow these steps:

  • Open Django Admin.
  • Create a question with several choices (like the given example).
  • Open the voting application.
  • Select Vote Now
  • Choose One option.
  • Click vote.
  • Check the displayed results.
Screenshot_2026-09-20_18-30-57
  • Selected option should have its count increased by one.

At last, We have built a functional voting system using Django with database-backed questions, multiple choices, an administrative interface, voting forms, and result pages.

Comment