├── pollme
├── __init__.py
├── views.py
├── wsgi.py
├── urls.py
└── settings.py
├── polls
├── __init__.py
├── migrations
│ ├── __init__.py
│ ├── 0006_poll_active.py
│ ├── 0004_poll_owner.py
│ ├── 0003_auto_20190117_2358.py
│ ├── 0001_initial.py
│ ├── 0005_vote.py
│ └── 0002_auto_20190117_0119.py
├── apps.py
├── admin.py
├── urls.py
├── templates
│ └── polls
│ │ ├── add_poll.html
│ │ ├── poll_detail.html
│ │ ├── add_choice.html
│ │ ├── endpoll.html
│ │ ├── poll_edit.html
│ │ ├── poll_result.html
│ │ └── polls_list.html
├── forms.py
├── tests.py
├── models.py
└── views.py
├── accounts
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── admin.py
├── tests.py
├── apps.py
├── urls.py
├── templates
│ └── accounts
│ │ ├── register.html
│ │ └── login.html
├── forms.py
└── views.py
├── .gitignore
├── static
├── img
│ └── background.jpg
└── css
│ └── home_style.css
├── manage.py
├── templates
├── home.html
├── includes
│ └── navbar.html
└── base.html
├── LICENSE
├── README.md
└── seeder.py
/pollme/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/polls/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/accounts/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/polls/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/accounts/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/accounts/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/accounts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/accounts/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | *.pot
3 | *.pyc
4 | __pycache__/
5 | local_settings.py
6 | db.sqlite3
7 | media
--------------------------------------------------------------------------------
/static/img/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MainakRepositor/Voter/HEAD/static/img/background.jpg
--------------------------------------------------------------------------------
/polls/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class PollsConfig(AppConfig):
5 | name = 'polls'
6 |
--------------------------------------------------------------------------------
/accounts/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AccountsConfig(AppConfig):
5 | name = 'accounts'
6 |
--------------------------------------------------------------------------------
/pollme/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 |
4 | def home(request):
5 | return render(request,'home.html')
--------------------------------------------------------------------------------
/polls/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Poll, Choice, Vote
3 |
4 | admin.site.register(Poll)
5 | admin.site.register(Choice)
6 | admin.site.register(Vote)
7 |
--------------------------------------------------------------------------------
/pollme/wsgi.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import os
4 |
5 | from django.core.wsgi import get_wsgi_application
6 |
7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pollme.settings')
8 |
9 | application = get_wsgi_application()
10 |
--------------------------------------------------------------------------------
/accounts/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from . import views
3 |
4 | app_name = "accounts"
5 |
6 | urlpatterns=[
7 | path('login/', views.login_user, name='login'),
8 | path('logout/', views.logout_user, name='logout'),
9 | path('register/', views.create_user, name='register'),
10 | ]
--------------------------------------------------------------------------------
/static/css/home_style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height:100%;
3 | padding: 0;
4 | margin: 0;
5 | color: white;
6 | }
7 | html{
8 | background-image: url('../img/background.jpg');
9 | background-size: cover;
10 | }
11 | body {
12 | background: rgba(0, 0, 0, 0.466);
13 | }
14 | #home-content{
15 | text-align: center;
16 | padding-top: 20%;
17 | }
--------------------------------------------------------------------------------
/pollme/urls.py:
--------------------------------------------------------------------------------
1 |
2 | from django.contrib import admin
3 | from django.urls import path, include
4 | from . import views
5 |
6 | urlpatterns = [
7 | path('admin/', admin.site.urls),
8 | path('home/', views.home, name='home'),
9 | path('polls/', include('polls.urls', namespace="polls")),
10 | path('accounts/', include('accounts.urls', namespace="accounts")),
11 | ]
12 |
--------------------------------------------------------------------------------
/polls/migrations/0006_poll_active.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-18 15:35
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('polls', '0005_vote'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='poll',
15 | name='active',
16 | field=models.BooleanField(default=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pollme.settings')
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/polls/migrations/0004_poll_owner.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-17 18:34
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('polls', '0003_auto_20190117_2358'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='poll',
18 | name='owner',
19 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
20 | preserve_default=False,
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/polls/migrations/0003_auto_20190117_2358.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-17 17:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('polls', '0002_auto_20190117_0119'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='choice',
15 | old_name='question',
16 | new_name='poll',
17 | ),
18 | migrations.RemoveField(
19 | model_name='choice',
20 | name='votes',
21 | ),
22 | migrations.AlterField(
23 | model_name='poll',
24 | name='text',
25 | field=models.TextField(),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/polls/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-16 16:59
2 |
3 | import datetime
4 | from django.db import migrations, models
5 | from django.utils.timezone import utc
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Poll',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('text', models.CharField(max_length=255)),
21 | ('pub_date', models.DateTimeField(default=datetime.datetime(2019, 1, 16, 16, 59, 36, 459183, tzinfo=utc))),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/templates/home.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% block custom_css %}
4 |
5 | {% endblock custom_css %}
6 |
7 |
8 | {% block content %}
9 |
10 |
11 |
12 |
13 |
14 |
SUS Biometric Polling Solutions
15 |
Vote Your Choice. Your Voice Matters.
16 |
17 |
Get Started
18 |
19 |
20 |
21 |
22 | {% endblock content %}
--------------------------------------------------------------------------------
/polls/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from . import views
3 |
4 | app_name = "polls"
5 |
6 | urlpatterns = [
7 | path('list/', views.polls_list, name='list'),
8 | path('list/user/', views.list_by_user, name='list_by_user'),
9 | path('add/', views.polls_add, name='add'),
10 | path('edit//', views.polls_edit, name='edit'),
11 | path('delete//', views.polls_delete, name='delete_poll'),
12 | path('end//', views.endpoll, name='end_poll'),
13 | path('edit//choice/add/', views.add_choice, name='add_choice'),
14 | path('edit/choice//', views.choice_edit, name='choice_edit'),
15 | path('delete/choice//',
16 | views.choice_delete, name='choice_delete'),
17 | path('/', views.poll_detail, name='detail'),
18 | path('/vote/', views.poll_vote, name='vote'),
19 | ]
20 |
--------------------------------------------------------------------------------
/polls/migrations/0005_vote.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-18 12:00
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('polls', '0004_poll_owner'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Vote',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('choice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Choice')),
21 | ('poll', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Poll')),
22 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
23 | ],
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/register.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
Already have an account? Login Here
8 | {% if messages %}
9 |
10 | {% for message in messages %}
11 |
{{ message }}
12 |
13 | ×
14 |
15 |
16 | {% endfor %}
17 |
18 | {% endif %}
19 |
24 |
25 |
26 |
27 | {% endblock %}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mainak Chaudhuri
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/polls/templates/polls/add_poll.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
Create new poll
8 | {% if messages %}
9 |
10 | {% for message in messages %}
11 | {{ message }}
12 | {% endfor %}
13 |
14 | {% endif %}
15 |
27 |
28 |
29 |
30 | {% endblock %}
--------------------------------------------------------------------------------
/polls/migrations/0002_auto_20190117_0119.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-16 19:19
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import django.utils.timezone
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('polls', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Choice',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('choice_text', models.CharField(max_length=255)),
20 | ('votes', models.IntegerField(default=0)),
21 | ],
22 | ),
23 | migrations.AlterField(
24 | model_name='poll',
25 | name='pub_date',
26 | field=models.DateTimeField(default=django.utils.timezone.now),
27 | ),
28 | migrations.AddField(
29 | model_name='choice',
30 | name='question',
31 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Poll'),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/polls/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from .models import Poll, Choice
3 |
4 |
5 | class PollAddForm(forms.ModelForm):
6 |
7 | choice1 = forms.CharField(label='Party 1', max_length=100, min_length=2,
8 | widget=forms.TextInput(attrs={'class': 'form-control'}))
9 | choice2 = forms.CharField(label='Party 2', max_length=100, min_length=2,
10 | widget=forms.TextInput(attrs={'class': 'form-control'}))
11 |
12 |
13 | class Meta:
14 | model = Poll
15 | fields = ['text', 'choice1', 'choice2']
16 | widgets = {
17 | 'text': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'cols': 20}),
18 | }
19 |
20 |
21 | class EditPollForm(forms.ModelForm):
22 | class Meta:
23 | model = Poll
24 | fields = ['text', ]
25 | widgets = {
26 | 'text': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'cols': 20}),
27 | }
28 |
29 |
30 | class ChoiceAddForm(forms.ModelForm):
31 | class Meta:
32 | model = Choice
33 | fields = ['choice_text', ]
34 | widgets = {
35 | 'choice_text': forms.TextInput(attrs={'class': 'form-control', })
36 | }
37 |
--------------------------------------------------------------------------------
/polls/templates/polls/poll_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 |
4 | {% block content %}
5 |
6 |
Polls details page
7 | {% if messages %}
8 |
9 | {% for message in messages %}
10 |
{{ message }}
11 |
12 | ×
13 |
14 |
15 | {% endfor %}
16 |
17 | {% endif %}
18 |
19 |
{{ poll }}
20 |
30 |
31 |
32 |
33 | {% endblock content %}
--------------------------------------------------------------------------------
/polls/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | import datetime
3 |
4 | from django.utils import timezone
5 | from django.urls import reverse
6 |
7 | from .models import Question
8 |
9 |
10 | class QuestionModelTest(TestCase):
11 | def test_was_published_recently_with_future_question(self):
12 | time = timezone.now() + datetime.timedelta(days=30)
13 | future_question = Question(pub_date=time)
14 | self.assertIs(future_question.was_published_recently(), False)
15 |
16 | """
17 | was_published_recently() returns False for questions whose pub_date
18 | is older than 1 day.
19 | """
20 |
21 |
22 | def test_was_published_recently_with_old_question(self):
23 | """
24 | was_published_recently() returns False for questions whose pub_date
25 | is older than 1 day.
26 | """
27 | time = timezone.now() - datetime.timedelta(days=1, seconds=1)
28 | old_question = Question(pub_date=time)
29 | self.assertIs(old_question.was_published_recently(), False)
30 |
31 |
32 | def test_was_published_recently_with_recent_question(self):
33 | """
34 | was_published_recently() returns True for questions whose pub_date
35 | is within the last day.
36 | """
37 | time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
38 | recent_question = Question(pub_date=time)
39 | self.assertIs(recent_question.was_published_recently(), True)
40 |
41 |
42 |
43 | # Create your tests here.
44 |
--------------------------------------------------------------------------------
/accounts/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib.auth.models import User
3 |
4 |
5 | class UserRegistrationForm(forms.Form):
6 | username = forms.CharField(label='Username', max_length=100, min_length=5,
7 | widget=forms.TextInput(attrs={'class': 'form-control'}))
8 | email = forms.EmailField(label='Email', max_length=20, min_length=5,
9 | widget=forms.EmailInput(attrs={'class': 'form-control'}))
10 | phone = forms.CharField(label='Phone Number', max_length=20, min_length=5,
11 | widget=forms.TextInput(attrs={'class': 'form-control'}))
12 | voter_id = forms.CharField(label='Voter ID', max_length=20, min_length=5,
13 | widget=forms.TextInput(attrs={'class': 'form-control'}))
14 | place = forms.CharField(label='Block / Constituency', max_length=20, min_length=5,
15 | widget=forms.TextInput(attrs={'class': 'form-control'}))
16 | CHOICES = [('M','Male'),('F','Female')]
17 | Gender=forms.CharField(label='Gender', widget=forms.RadioSelect(choices=CHOICES))
18 | password1 = forms.CharField(label='Password', max_length=50, min_length=5,
19 | widget=forms.PasswordInput(attrs={'class': 'form-control'}))
20 | password2 = forms.CharField(label='Confirm Password',
21 | max_length=50, min_length=5,
22 | widget=forms.PasswordInput(attrs={'class': 'form-control'}))
23 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/login.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 | {% if messages %}
8 |
9 | {% for message in messages %}
10 |
{{ message }}
11 |
12 | ×
13 |
14 |
15 | {% endfor %}
16 |
17 | {% endif %}
18 |
31 |
32 |
33 |
34 | {% endblock %}
--------------------------------------------------------------------------------
/polls/templates/polls/add_choice.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 | {% if edit_choice %}
8 |
Update choice
9 | {% else %}
10 |
Add new choice
11 | {% endif %}
12 | {% if messages %}
13 |
14 | {% for message in messages %}
15 | {{ message }}
16 | {% endfor %}
17 |
18 | {% endif %}
19 |
35 |
36 |
37 |
38 | {% endblock %}
--------------------------------------------------------------------------------
/templates/includes/navbar.html:
--------------------------------------------------------------------------------
1 |
2 | Welcome : {{user.username}}
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | {% if request.user.is_authenticated %}
23 |
24 |
25 |
Logout
26 | {% else %}
27 |
Login
28 |
Register
29 | {% endif %}
30 |
31 |
32 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Fingerprint Voting System
10 |
12 |
14 |
15 | {% block custom_css %}{% endblock custom_css %}
16 |
17 |
18 |
19 |
20 | {% include 'includes/navbar.html' %}
21 |
22 | {% block content %}
23 |
24 | {% endblock content %}
25 |
26 |
27 |
28 |
30 |
32 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/polls/templates/polls/endpoll.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 |
4 | {% block content %}
5 |
6 | {% if messages %}
7 |
8 | {% for message in messages %}
9 |
{{ message }}
10 |
11 | ×
12 |
13 |
14 | {% endfor %}
15 |
16 | {% else %}
17 |
18 |
19 |
Result for: {{ poll.text }}
20 |
21 |
22 |
23 | {% for choice in poll.get_result_dict %}
24 |
{{ choice.text }}-{{ choice.percentage|floatformat }}%
26 | {% endfor %}
27 |
28 |
29 |
30 | {% for choice in poll.choice_set.all %}
31 |
32 | {{ choice.choice_text }}
33 | {{ choice.get_vote_count }}
34 |
35 | {% endfor %}
36 |
37 | {% endif %}
38 |
Back To Polls
39 |
40 |
41 |
42 |
43 | {% endblock content %}
--------------------------------------------------------------------------------
/polls/templates/polls/poll_edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
Edit poll
8 | {% if messages %}
9 | {% if messages %}
10 |
11 | {% for message in messages %}
12 |
{{ message }}
13 |
14 | ×
15 |
16 |
17 | {% endfor %}
18 |
19 | {% endif %}
20 | {% endif %}
21 |
34 |
35 |
36 |
Choices
37 |
38 |
39 | {% for choice in poll.choice_set.all %}
40 |  
41 | {{ choice.choice_text }}
42 | {% endfor %}
43 |
44 |
45 |
46 |
47 |
48 |
49 | {% endblock %}
--------------------------------------------------------------------------------
/polls/templates/polls/poll_result.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | {% if messages %}
6 |
7 | {% for message in messages %}
8 |
{{ message }}
9 |
10 | ×
11 |
12 |
13 | {% endfor %}
14 |
15 | {% else %}
16 |
17 |
18 | {% if poll.active %}
19 |
Result for: {{ poll.text }}
20 | {% else %}
21 |
"{{ poll.text }}" Has Ended Polling !
22 | {% endif %}
23 |
Total: {{ poll.get_vote_count }} votes
24 |
25 |
26 | {% for choice in poll.get_result_dict %}
27 |
29 | {{choice.text|truncatewords:2}}-{{choice.percentage|floatformat}}%
30 |
31 | {% endfor %}
32 |
33 |
34 |
35 | {% for choice in poll.choice_set.all %}
36 |
37 | {{ choice.choice_text }}
38 | {{ choice.get_vote_count }}
39 |
40 | {% endfor %}
41 |
42 | {% endif %}
43 |
Back To Polls
44 |
45 |
46 |
47 |
48 | {% endblock content %}
--------------------------------------------------------------------------------
/polls/models.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from django.db import models
3 | from django.utils import timezone
4 | import secrets
5 |
6 |
7 | class Poll(models.Model):
8 | owner = models.ForeignKey(User, on_delete=models.CASCADE)
9 | text = models.TextField()
10 | pub_date = models.DateTimeField(default=timezone.now)
11 | active = models.BooleanField(default=True)
12 |
13 | def user_can_vote(self, user):
14 | """
15 | Return False if user already voted
16 | """
17 | user_votes = user.vote_set.all()
18 | qs = user_votes.filter(poll=self)
19 | if qs.exists():
20 | return False
21 | return True
22 |
23 | @property
24 | def get_vote_count(self):
25 | return self.vote_set.count()
26 |
27 | def get_result_dict(self):
28 | res = []
29 | for choice in self.choice_set.all():
30 | d = {}
31 | alert_class = ['primary', 'secondary', 'success',
32 | 'danger', 'dark', 'warning', 'info']
33 |
34 | d['alert_class'] = secrets.choice(alert_class)
35 | d['text'] = choice.choice_text
36 | d['num_votes'] = choice.get_vote_count
37 | if not self.get_vote_count:
38 | d['percentage'] = 0
39 | else:
40 | d['percentage'] = (choice.get_vote_count /
41 | self.get_vote_count)*100
42 |
43 | res.append(d)
44 | return res
45 |
46 | def __str__(self):
47 | return self.text
48 |
49 |
50 | class Choice(models.Model):
51 | poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
52 | choice_text = models.CharField(max_length=255)
53 |
54 | @property
55 | def get_vote_count(self):
56 | return self.vote_set.count()
57 |
58 | def __str__(self):
59 | return f"{self.poll.text[:25]} - {self.choice_text[:25]}"
60 |
61 |
62 | class Vote(models.Model):
63 | user = models.ForeignKey(User, on_delete=models.CASCADE)
64 | poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
65 | choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
66 |
67 |
68 |
69 |
70 | def __str__(self):
71 | return f'{self.poll.text[:15]} - {self.choice.choice_text[:15]} - {self.user.username}'
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/accounts/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import authenticate, login, logout
2 | from django.shortcuts import render, redirect
3 | from django.contrib.auth.models import User
4 | from .forms import UserRegistrationForm
5 | from django.contrib import messages
6 | from django.http import HttpResponse
7 |
8 |
9 | def login_user(request):
10 | if request.method == 'POST':
11 | username = request.POST.get('username')
12 | password = request.POST.get('password')
13 | user = authenticate(username=username, password=password)
14 |
15 | if user is not None:
16 | login(request, user)
17 | redirect_url = request.GET.get('next', 'home')
18 | return redirect(redirect_url)
19 | else:
20 | messages.error(request, "Username Or Password is incorrect!!",
21 | extra_tags='alert alert-warning alert-dismissible fade show')
22 |
23 | return render(request, 'accounts/login.html')
24 |
25 |
26 | def logout_user(request):
27 | logout(request)
28 | return redirect('home')
29 |
30 |
31 | def create_user(request):
32 | if request.method == 'POST':
33 | check1 = False
34 | check2 = False
35 | check3 = False
36 | form = UserRegistrationForm(request.POST)
37 | if form.is_valid():
38 | username = form.cleaned_data['username']
39 | password1 = form.cleaned_data['password1']
40 | password2 = form.cleaned_data['password2']
41 | email = form.cleaned_data['email']
42 |
43 | if password1 != password2:
44 | check1 = True
45 | messages.error(request, 'Password doesn\'t matched',
46 | extra_tags='alert alert-warning alert-dismissible fade show')
47 | if User.objects.filter(username=username).exists():
48 | check2 = True
49 | messages.error(request, 'Username already exists',
50 | extra_tags='alert alert-warning alert-dismissible fade show')
51 | if User.objects.filter(email=email).exists():
52 | check3 = True
53 | messages.error(request, 'Email already registered',
54 | extra_tags='alert alert-warning alert-dismissible fade show')
55 |
56 | if check1 or check2 or check3:
57 | messages.error(
58 | request, "Registration Failed", extra_tags='alert alert-warning alert-dismissible fade show')
59 | return redirect('accounts:register')
60 | else:
61 | user = User.objects.create_user(
62 | username=username, password=password1, email=email)
63 | messages.success(
64 | request, f'Thanks for registering {user.username}!', extra_tags='alert alert-success alert-dismissible fade show')
65 | return redirect('accounts:login')
66 | else:
67 | form = UserRegistrationForm()
68 | return render(request, 'accounts/register.html', {'form': form})
69 |
--------------------------------------------------------------------------------
/pollme/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for pollme project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.1.5.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.1/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'x*za6xf&_80ofdpae!yzq61g9ffikkx9$*iygbl$j7rr4wlf8t'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'polls.apps.PollsConfig',
41 | 'accounts.apps.AccountsConfig',
42 | ]
43 |
44 | MIDDLEWARE = [
45 | 'django.middleware.security.SecurityMiddleware',
46 | 'django.contrib.sessions.middleware.SessionMiddleware',
47 | 'django.middleware.common.CommonMiddleware',
48 | 'django.middleware.csrf.CsrfViewMiddleware',
49 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
50 | 'django.contrib.messages.middleware.MessageMiddleware',
51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
52 | ]
53 |
54 | ROOT_URLCONF = 'pollme.urls'
55 |
56 | TEMPLATES = [
57 | {
58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
59 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
60 | 'APP_DIRS': True,
61 | 'OPTIONS': {
62 | 'context_processors': [
63 | 'django.template.context_processors.debug',
64 | 'django.template.context_processors.request',
65 | 'django.contrib.auth.context_processors.auth',
66 | 'django.contrib.messages.context_processors.messages',
67 | ],
68 | },
69 | },
70 | ]
71 |
72 | WSGI_APPLICATION = 'pollme.wsgi.application'
73 |
74 |
75 | # Database
76 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
77 |
78 | DATABASES = {
79 | 'default': {
80 | 'ENGINE': 'django.db.backends.sqlite3',
81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
82 | }
83 | }
84 |
85 |
86 | # Password validation
87 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
88 |
89 | AUTH_PASSWORD_VALIDATORS = [
90 | {
91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
92 | },
93 | {
94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
95 | },
96 | {
97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
98 | },
99 | {
100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
101 | },
102 | ]
103 |
104 |
105 |
106 |
107 | LANGUAGE_CODE = 'en-us'
108 |
109 | TIME_ZONE = 'Asia/Dhaka'
110 |
111 | USE_I18N = True
112 |
113 | USE_L10N = True
114 |
115 | USE_TZ = True
116 |
117 |
118 |
119 |
120 | STATIC_URL = '/static/'
121 | STATICFILES_DIRS = [
122 | os.path.join(BASE_DIR, 'static')
123 | ]
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Django-Poll-App
2 | Django poll app is a full featured polling app. You have to register in this app to show the polls and to vote. If you already voted you can not vote again. Only the owner of a poll can add poll , edit poll, update poll, delete poll , add choice, update choice, delete choice and end a poll. If a poll is ended it can not be voted. Ended poll only shows user the final result of the poll. There is a search option for polls. Also user can filter polls by name, publish date, and by number of voted. Pagination will work even after applying filter.
3 |
4 | Getting Started
5 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
6 |
7 | Prerequisites
8 | python== 3.5 or up and django==2.0 or up
9 |
10 | Installing
11 | open terminal and type
12 | git clone https://github.com/devmahmud/Django-poll-app.git
13 |
14 | or simply download using the url below
15 | https://github.com/devmahmud/Django-poll-app.git
16 |
17 | To migrate the database open terminal in project directory and type
18 | python manage.py makemigrations
19 | python manage.py migrate
20 |
21 | To use admin panel you need to create superuser using this command
22 | python manage.py createsuperuser
23 |
24 | To Create some dummy text data for your app follow the step below:
25 | pip install faker
26 | python manage.py shell
27 | import seeder
28 | seeder.seed_all(30)
29 | Here 30 is a number of entry. You can use it as your own
30 |
31 | To run the program in local server use the following command
32 | python manage.py runserver
33 |
34 | Then go to http://127.0.0.1:8000/home in your browser
35 |
36 | Project snapshot
37 | Home page
38 |
39 |
40 |
41 |
42 | Login Page
43 |
44 |
45 |
46 |
47 | Registration Page
48 |
49 |
50 |
51 |
52 | Poll List Page
53 |
54 |
55 |
56 |
57 | Poll Add Page
58 |
59 |
60 |
61 |
62 | Polling page
63 |
64 |
65 |
66 |
67 | Poll Result Page
68 |
69 |
70 |
71 |
72 | Poll Edit Page
73 |
74 |
75 |
76 |
77 | Choice Update Delete Page
78 |
79 |
80 |
81 |
82 | Author
83 |
84 | Mainak Chaudhuri
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/seeder.py:
--------------------------------------------------------------------------------
1 | from polls.models import Choice, Poll, Vote
2 | from django.contrib.auth.models import User
3 | import datetime
4 | import random
5 | import time
6 | from faker import Faker
7 | fake = Faker()
8 |
9 |
10 | def seed_users(num_entries=10, overwrite=False):
11 | """
12 | Creates num_entries worth a new users
13 | """
14 | if overwrite:
15 | print("Overwriting Users")
16 | Users.objects.all().delete()
17 | count = 0
18 | for _ in range(num_entries):
19 | first_name = fake.first_name()
20 | last_name = fake.last_name()
21 | u = User.objects.create_user(
22 | first_name=first_name,
23 | last_name=last_name,
24 | email=first_name + "." + last_name + "@fakermail.com",
25 | username=first_name + last_name,
26 | password="password"
27 | )
28 | count += 1
29 | percent_complete = count / num_entries * 100
30 | print(
31 | "Adding {} new Users: {:.2f}%".format(
32 | num_entries, percent_complete),
33 | end='\r',
34 | flush=True
35 | )
36 | print()
37 |
38 |
39 | def seed_polls(num_entries=10, choice_min=2, choice_max=5, overwrite=False):
40 | """
41 | Seeds num_entries poll with random users as owners
42 | Each poll will be seeded with # choices from choice_min to choice_max
43 | """
44 | if overwrite:
45 | print('Overwriting polls')
46 | Poll.objects.all().delete()
47 | users = list(User.objects.all())
48 | count = 0
49 | for _ in range(num_entries):
50 | p = Poll(
51 | owner=random.choice(users),
52 | text=fake.paragraph(),
53 | pub_date=datetime.datetime.now()
54 | )
55 | p.save()
56 | num_choices = random.randrange(choice_min, choice_max + 1)
57 | for _ in range(num_choices):
58 | c = Choice(
59 | poll=p,
60 | choice_text=fake.sentence()
61 | ).save()
62 | count += 1
63 | percent_complete = count / num_entries * 100
64 | print(
65 | "Adding {} new Polls: {:.2f}%".format(
66 | num_entries, percent_complete),
67 | end='\r',
68 | flush=True
69 | )
70 | print()
71 |
72 |
73 | def seed_votes():
74 | """
75 | Creates a new vote on every poll for every user
76 | Voted for choice is selected random.
77 | Deletes all votes prior to adding new ones
78 | """
79 | Vote.objects.all().delete()
80 | users = User.objects.all()
81 | polls = Poll.objects.all()
82 | count = 0
83 | number_of_new_votes = users.count() * polls.count()
84 | for poll in polls:
85 | choices = list(poll.choice_set.all())
86 | for user in users:
87 | v = Vote(
88 | user=user,
89 | poll=poll,
90 | choice=random.choice(choices)
91 | ).save()
92 | count += 1
93 | percent_complete = count / number_of_new_votes * 100
94 | print(
95 | "Adding {} new votes: {:.2f}%".format(
96 | number_of_new_votes, percent_complete),
97 | end='\r',
98 | flush=True
99 | )
100 | print()
101 |
102 |
103 | def seed_all(num_entries=10, overwrite=False):
104 | """
105 | Runs all seeder functions. Passes value of overwrite to all
106 | seeder function calls.
107 | """
108 | start_time = time.time()
109 | # run seeds
110 | seed_users(num_entries=num_entries, overwrite=overwrite)
111 | seed_polls(num_entries=num_entries, overwrite=overwrite)
112 | seed_votes()
113 | # get time
114 | elapsed_time = time.time() - start_time
115 | minutes = int(elapsed_time // 60)
116 | seconds = int(elapsed_time % 60)
117 | print("Script Execution took: {} minutes {} seconds".format(minutes, seconds))
118 |
--------------------------------------------------------------------------------
/polls/templates/polls/polls_list.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
Hi {{user.username}}, Welcome to polling List!
8 | {% if messages %}
9 |
10 | {% for message in messages %}
11 |
{{ message }}
12 |
13 | ×
14 |
15 |
16 | {% endfor %}
17 |
18 | {% endif %}
19 |
20 |
22 | Name
23 |
Date
25 |
Vote
27 |
28 |
Add
29 |
30 |
36 |
37 |
38 |
57 |
58 |
71 |
72 |
User Details
73 |
74 |
75 | Username : {{user.username}}
76 | Email : {{user.email}}
77 | Machine Generated Voter Id : ECI-2021-{{user.id}}-IND
78 | Last Login : {{user.last_login}}
79 | Join Date : {{user.date_joined}}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | {% endblock content %}
--------------------------------------------------------------------------------
/polls/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, get_object_or_404, redirect
2 | from django.contrib.auth.decorators import login_required
3 | from django.core.paginator import Paginator
4 | from django.db.models import Count
5 | from django.contrib import messages
6 | from .models import Poll, Choice, Vote
7 | from .forms import PollAddForm, EditPollForm, ChoiceAddForm
8 | from django.http import HttpResponse
9 |
10 |
11 | @login_required()
12 | def polls_list(request):
13 | all_polls = Poll.objects.all()
14 | search_term = ''
15 | if 'name' in request.GET:
16 | all_polls = all_polls.order_by('text')
17 |
18 | if 'date' in request.GET:
19 | all_polls = all_polls.order_by('pub_date')
20 |
21 | if 'vote' in request.GET:
22 | all_polls = all_polls.annotate(Count('vote')).order_by('vote__count')
23 |
24 | if 'search' in request.GET:
25 | search_term = request.GET['search']
26 | all_polls = all_polls.filter(text__icontains=search_term)
27 |
28 | paginator = Paginator(all_polls, 6) # Show 6 contacts per page
29 | page = request.GET.get('page')
30 | polls = paginator.get_page(page)
31 |
32 | get_dict_copy = request.GET.copy()
33 | params = get_dict_copy.pop('page', True) and get_dict_copy.urlencode()
34 | print(params)
35 | context = {
36 | 'polls': polls,
37 | 'params': params,
38 | 'search_term': search_term,
39 | }
40 | return render(request, 'polls/polls_list.html', context)
41 |
42 |
43 | @login_required()
44 | def list_by_user(request):
45 | all_polls = Poll.objects.filter(owner=request.user)
46 | paginator = Paginator(all_polls, 7) # Show 7 contacts per page
47 |
48 | page = request.GET.get('page')
49 | polls = paginator.get_page(page)
50 |
51 | context = {
52 | 'polls': polls,
53 | }
54 | return render(request, 'polls/polls_list.html', context)
55 |
56 |
57 | @login_required()
58 | def polls_add(request):
59 | if request.user.has_perm('polls.add_poll'):
60 | if request.method == 'POST':
61 | form = PollAddForm(request.POST)
62 | if form.is_valid:
63 | poll = form.save(commit=False)
64 | poll.owner = request.user
65 | poll.save()
66 | new_choice1 = Choice(
67 | poll=poll, choice_text=form.cleaned_data['choice1']).save()
68 | new_choice2 = Choice(
69 | poll=poll, choice_text=form.cleaned_data['choice2']).save()
70 |
71 | messages.success(
72 | request, "Poll & Choices added successfully", extra_tags='alert alert-success alert-dismissible fade show')
73 |
74 | return redirect('polls:list')
75 | else:
76 | form = PollAddForm()
77 | context = {
78 | 'form': form,
79 | }
80 | return render(request, 'polls/add_poll.html', context)
81 | else:
82 | return HttpResponse("Sorry but you don't have permission to do that!")
83 |
84 |
85 | @login_required
86 | def polls_edit(request, poll_id):
87 | poll = get_object_or_404(Poll, pk=poll_id)
88 | if request.user != poll.owner:
89 | return redirect('home')
90 |
91 | if request.method == 'POST':
92 | form = EditPollForm(request.POST, instance=poll)
93 | if form.is_valid:
94 | form.save()
95 | messages.success(request, "Poll Updated successfully",
96 | extra_tags='alert alert-success alert-dismissible fade show')
97 | return redirect("polls:list")
98 |
99 | else:
100 | form = EditPollForm(instance=poll)
101 |
102 | return render(request, "polls/poll_edit.html", {'form': form, 'poll': poll})
103 |
104 |
105 | @login_required
106 | def polls_delete(request, poll_id):
107 | poll = get_object_or_404(Poll, pk=poll_id)
108 | if request.user != poll.owner:
109 | return redirect('home')
110 | poll.delete()
111 | messages.success(request, "Poll Deleted successfully",
112 | extra_tags='alert alert-success alert-dismissible fade show')
113 | return redirect("polls:list")
114 |
115 |
116 | @login_required
117 | def add_choice(request, poll_id):
118 | poll = get_object_or_404(Poll, pk=poll_id)
119 | if request.user != poll.owner:
120 | return redirect('home')
121 |
122 | if request.method == 'POST':
123 | form = ChoiceAddForm(request.POST)
124 | if form.is_valid:
125 | new_choice = form.save(commit=False)
126 | new_choice.poll = poll
127 | new_choice.save()
128 | messages.success(
129 | request, "Choice added successfully", extra_tags='alert alert-success alert-dismissible fade show')
130 | return redirect('polls:edit', poll.id)
131 | else:
132 | form = ChoiceAddForm()
133 | context = {
134 | 'form': form,
135 | }
136 | return render(request, 'polls/add_choice.html', context)
137 |
138 |
139 | @login_required
140 | def choice_edit(request, choice_id):
141 | choice = get_object_or_404(Choice, pk=choice_id)
142 | poll = get_object_or_404(Poll, pk=choice.poll.id)
143 | if request.user != poll.owner:
144 | return redirect('home')
145 |
146 | if request.method == 'POST':
147 | form = ChoiceAddForm(request.POST, instance=choice)
148 | if form.is_valid:
149 | new_choice = form.save(commit=False)
150 | new_choice.poll = poll
151 | new_choice.save()
152 | messages.success(
153 | request, "Choice Updated successfully", extra_tags='alert alert-success alert-dismissible fade show')
154 | return redirect('polls:edit', poll.id)
155 | else:
156 | form = ChoiceAddForm(instance=choice)
157 | context = {
158 | 'form': form,
159 | 'edit_choice': True,
160 | 'choice': choice,
161 | }
162 | return render(request, 'polls/add_choice.html', context)
163 |
164 |
165 | @login_required
166 | def choice_delete(request, choice_id):
167 | choice = get_object_or_404(Choice, pk=choice_id)
168 | poll = get_object_or_404(Poll, pk=choice.poll.id)
169 | if request.user != poll.owner:
170 | return redirect('home')
171 | choice.delete()
172 | messages.success(
173 | request, "Choice Deleted successfully", extra_tags='alert alert-success alert-dismissible fade show')
174 | return redirect('polls:edit', poll.id)
175 |
176 |
177 | def poll_detail(request, poll_id):
178 | poll = get_object_or_404(Poll, id=poll_id)
179 |
180 | if not poll.active:
181 | return render(request, 'polls/poll_result.html', {'poll': poll})
182 | loop_count = poll.choice_set.count()
183 | context = {
184 | 'poll': poll,
185 | 'loop_time': range(0, loop_count),
186 | }
187 | return render(request, 'polls/poll_detail.html', context)
188 |
189 |
190 | @login_required
191 | def poll_vote(request, poll_id):
192 | poll = get_object_or_404(Poll, pk=poll_id)
193 | choice_id = request.POST.get('choice')
194 | if not poll.user_can_vote(request.user):
195 | messages.error(
196 | request, "You already voted this poll", extra_tags='alert alert-warning alert-dismissible fade show')
197 | return redirect("polls:list")
198 |
199 | if choice_id:
200 | choice = Choice.objects.get(id=choice_id)
201 | vote = Vote(user=request.user, poll=poll, choice=choice)
202 | vote.save()
203 | print(vote)
204 | return render(request, 'polls/poll_result.html', {'poll': poll})
205 | else:
206 | messages.error(
207 | request, "No choice selected", extra_tags='alert alert-warning alert-dismissible fade show')
208 | return redirect("polls:detail", poll_id)
209 | return render(request, 'polls/poll_result.html', {'poll': poll})
210 |
211 |
212 | @login_required
213 | def endpoll(request, poll_id):
214 | poll = get_object_or_404(Poll, pk=poll_id)
215 | if request.user != poll.owner:
216 | return redirect('home')
217 |
218 | if poll.active is True:
219 | poll.active = False
220 | poll.save()
221 | return render(request, 'polls/poll_result.html', {'poll': poll})
222 | else:
223 | return render(request, 'polls/poll_result.html', {'poll': poll})
224 |
--------------------------------------------------------------------------------