- Tutorial based on django polls https://docs.djangoproject.com/en/3.2/intro/tutorial01/
- Original polls repo: https://github.com/do-community/django-polls/tree/master/polls
- Staff user
- Can add a new poll
- Can edit a poll
- Can delete a poll
- Regular user
- Can vote
- Can view a poll results
- Anonymous user
- Can only view a poll result
- Enable only staff user to interact with polls
We have 3 views: detail, results and vote. User object can be fetched with the following.
defdetail(request, question_id):
user=request.user
...We can restrict access with the following piece of code. Apply this to all 3 views.
defdetail(request, question_id):
ifnotrequest.user.is_staff:
raisePermissionDenied('You are not allowed to perform this action.')
...- We don't want to click every time. Let's create test for vote, results and detail functionality. Read first:https://docs.djangoproject.com/en/3.2/topics/testing/overview/
Go to file tests.py and add the following code.
fromdjango.testimportTestCase, Clientfromdjango.contrib.auth.modelsimportUser
...
classStaffUserTests(TestCase):
defsetUp(self):
username='staff_user'password='test123'self.question=Question.objects.create(question_text='text', pub_date=timezone.now())
self.choice=Choice.objects.create(question=self.question, choice_text='text')
self.staff=User.objects.create_user(
username=username,
password=password,
is_staff=True,
)
self.client=Client()
deftest_detail_view(self):
self.client.force_login(self.staff)
response=self.client.get(reverse('polls:detail', kwargs={'question_id': self.question.pk}))
self.assertEqual(response.status_code, 200)You can run tests with the following commands:
python manage.py test polls
python manage.py test polls.tests.StaffUserTests
python manage.py test polls.tests.StaffUserTests.test_detail_view
Add tests for results and vote.
...
def test_results_view(self):
self.client.force_login(self.staff)
response = self.client.get(reverse('polls:results', kwargs={'question_id': self.question.pk}))
self.assertEqual(response.status_code, 200)
def test_vote_view(self):
self.client.force_login(self.staff)
response = self.client.post(
reverse('polls:vote', kwargs={'question_id': self.question.pk}),
{'choice': self.choice.pk}
)
self.assertEqual(response.status_code, 302)- Management commands Let's write custom management command so we can reset out db whenever we wish. Read first:https://docs.djangoproject.com/en/3.2/howto/custom-management-commands/#management-commands-and-locales
First, we need to create folder management/commands and inside of it file named by our custom command - populate_db.py
fromdjango.contrib.auth.modelsimportUserfromdjango.core.management.baseimportBaseCommandstaff_username='staff_user'regular_user='regular_user'password='test123'classCommand(BaseCommand):
help='Populates database with dummy data'defcreate_users(self):
self.staff=User.objects.create_user(
username=staff_username,
password=password,
is_staff=True,
)
self.regular_user=User.objects.create_user(
username=regular_user,
password=password,
is_staff=False,
is_superuser=False
)
defhandle(self, *args, **options):
self.create_users()
print('Users created')Now we can run our command by: python manage.py populate_db
- Enable anonymous user to view everything We have to allow anonymous user to check poll results. So remove restriction in detail and results view.
defdetail(request, question_id):
ifnotrequest.user.is_staff:
raisePermissionDenied('You are not allowed to perform this action.')
...
defresults(request, question_id):
ifnotrequest.user.is_staff:
raisePermissionDenied('You are not allowed to perform this action.')- Add vote restriction for anonymous user
We have 2 options with this: using decorators or manual check.
- Using decorators - redirect to login
fromdjango.contrib.auth.decoratorsimportlogin_required
...
@login_required(login_url='login')defvote(request, question_id):
...- Manual check
defvote(request, question_id):
ifnotrequest.user.is_authenticated:
raisePermissionDenied('You are not allowed to perform this action.')- Django admin
6.1 Add search in questions
# admin.pyclassQuestionAdmin(admin.ModelAdmin):
list_display= ('question_text', 'pub_date', 'was_published_recently')
search_fields= ['question_text']6.2 Add filters in questions
# admin.pyclassQuestionAdmin(admin.ModelAdmin):
list_display= ('question_text', 'pub_date', 'was_published_recently')
list_filter= ['pub_date', ]
search_fields= ['question_text']6.3 Show choices next to the questions
fromdjango.utils.htmlimportformat_htmlfrom .modelsimportQuestion, ChoiceclassQuestionAdmin(admin.ModelAdmin):
list_display= ('question_text', 'pub_date', 'was_published_recently', 'get_choices')
list_filter= ['pub_date',]
search_fields= ['question_text']
defget_choices(self, obj):
html=''choices=Choice.objects.filter(question=obj)
forchoiceinchoices:
html+=f'{choice.choice_text}<br>'returnformat_html(html)6.4 Remove delete permission
classQuestionAdmin(admin.ModelAdmin):
...
defhas_delete_permission(self, request, obj=None):
returnFalse