A lightweight, token-based voting platform built with Django
Every voter gets one unique, single-use token per poll — ensuring exactly one vote per person.
- Overview
- Features
- Tech stack
- Project structure
- Local setup
- Production deployment
- Environment variables
- Security notes
- License
PollSystem is a self-hosted internal voting tool. An administrator creates a poll and the system automatically issues every registered voter a unique, single-use token. Voters log in, see the polls they're eligible for, and cast their vote using that token. Once a token is used, it can never be reused — so every voter gets exactly one vote per poll.
Note on privacy: voting is private, not cryptographically anonymous. A vote record stores only the token and choice (no direct user link), but since each token is tied to a specific voter in the database, an admin with database access could technically trace a vote back to a voter.
| Role | Capabilities |
|---|---|
| Admin | Add voters · create polls with any number of options · end polls · view live results & tallies |
| Voter | View active polls · cast one vote per poll · change password |
- ✅ One vote per token, enforced server-side
- ✅ Passwords hashed before storage
- ✅ Clean, dependency-free HTML/CSS UI — no JS framework required
- ✅ Live vote tallying once a poll ends
Python 3 · Django 5.2 · SQLite · Gunicorn · systemd
pollproject/
├── manage.py
├── requirements.txt
├── .env.example # copy to .env and fill in your own values
├── pollsite/ # project settings, URLs, WSGI
│ └── settings.py
└── polls/ # the app
├── models.py # User, Poll, Option, Token, Vote
├── views.py # all page logic
├── urls.py
├── management/commands/create_admin.py # CLI: create the first admin
└── templates/ # login, dashboard, vote, admin panel, about, etc.
git clone git@github.com:JsayeedR/pollsystem.git
cd pollsystem
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then edit .env with a real DJANGO_SECRET_KEY
python manage.py migrate
python manage.py create_admin
python manage.py runserverVisit http://127.0.0.1:8000/ and log in with the admin account you just created.
Runs as a systemd service (pollproject.service) calling Gunicorn on
127.0.0.1:8000, sitting behind a reverse proxy that serves it under /poll/.
Deploy / update flow:
git pull
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart pollproject.serviceSet these in a .env file on the server — never committed to Git.
| Variable | Purpose | Example |
|---|---|---|
DJANGO_SECRET_KEY |
Django's cryptographic secret key | long random string |
DJANGO_DEBUG |
Debug mode | False in production |
DJANGO_ALLOWED_HOSTS |
Comma-separated allowed hostnames | app.bsccl.com,127.0.0.1 |
DJANGO_STATIC_ROOT |
Optional override for static files dir | /home/app-admin/pollproject/staticfiles/ |
- Real secrets live only in
.envon the server, which is.gitignored - The production
db.sqlite3(real voter data) is never committed - Consider migrating password hashing to Django's built-in
django.contrib.authhashers (PBKDF2 / Argon2) for stronger, salted storage - See Overview for a note on the limits of vote privacy
This project is open source, licensed under the MIT License.
Developed by Jikrul Sayeed · KUET · EEE · 2k15 linkedin.com/in/jikrulsayeed