В предыдущей части урока по Django мы рассмотрели, как можно защитить наше приложение опросов от хакера. В этом уроке мы защитим наши опросы от повторного голосования.
1. Чтобы пользователь не мог повторно отправить форму, нужно его идентифицировать и где-то в системе записать, в каких опросах он проголосовал.
Идентификацию пользователя мы можем делать по ip. Хранить данные о пользователе можно в таблице с двумя полями: ip, question_id. Для создания этой таблицы напишем модель User в файле polls/models.py:
2. Чтобы наша модель отобразилась в админке, зарегистрируем ее в файле polls/admin.py:
3. Добавим в представления логику отслеживания пользователя - файл polls/views.py:
4. Отредактируем шаблон детального представления опроса: polls/templates/polls/detail.html:
Проверьте теперь работу приложения опросов. Вам не удастся дважды проголосовать за один и тот же опрос. Чтобы проголосовать еще, вам нужно удалить в админке соответствующие записи модели User.
1. Чтобы пользователь не мог повторно отправить форму, нужно его идентифицировать и где-то в системе записать, в каких опросах он проголосовал.
Идентификацию пользователя мы можем делать по ip. Хранить данные о пользователе можно в таблице с двумя полями: ip, question_id. Для создания этой таблицы напишем модель User в файле polls/models.py:
# ... class User(models.Model): """Пользователь, участвующий в опросе""" ip = models.GenericIPAddressField(verbose_name='IP пользователя') question = models.ForeignKey(Question, verbose_name='Вопрос голосования') def __unicode__(self): return self.ip def get_user_ip(self, request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[-1].strip() else: ip = request.META.get('REMOTE_ADDR') return ip def voted_already(self): """Голосовал ли пользователь с данным ip в опросе""" user_list = User.objects.filter(ip=self.ip, question = self.question) return len(user_list) > 0 class Meta: verbose_name = 'Пользователь' verbose_name_plural = 'Пользователи'
Теперь дадим команду на генерацию таблицы этой модели в БД: manage.py syncdb
2. Чтобы наша модель отобразилась в админке, зарегистрируем ее в файле polls/admin.py:
# ... class UserAdmin(admin.ModelAdmin): list_display = ('ip', 'question') admin.site.register(User, UserAdmin)
3. Добавим в представления логику отслеживания пользователя - файл polls/views.py:
# ... class PollDetailView(DetailView): model = Question template_name = 'polls/detail.html' def get_context_data(self, **kwargs): context = super(PollDetailView, self).get_context_data(**kwargs) user = User() user.ip = user.get_user_ip(self.request) user.question = Question.objects.get(pk=self.kwargs['pk']) # Передаем в шаблон переменную context['voted_already'] = user.voted_already() return context def vote(request, poll_id): """Обработка данных формы опроса""" question = get_object_or_404(Question, pk=poll_id) if not question.is_active: return HttpResponse('Опрос снят с публикации') user = User() user.ip = user.get_user_ip(request) user.question = question if user.voted_already(): return HttpResponse('Вы уже голосовали в этом опросе') if request.POST.get('answer'): try: selected_answer = question.answer_set.get(pk=request.POST['answer']) except(Answer.DoesNotExist, UnicodeEncodeError, ValueError): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "Указан недопустимый ответ", }) selected_answer.votes += 1 selected_answer.save() user.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) else: return render(request, 'polls/detail.html', { 'question': question, 'error_message': "Вы не выбрали ответ.", })
4. Отредактируем шаблон детального представления опроса: polls/templates/polls/detail.html:
{% if question.is_active %} {% if voted_already %} <p>Вы уже голосовали в этом опросе.</p> {% else %} <p>Просим вас принять участие в опросе</p> <h1>{{ question.title }}</h1> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% for answer in question.answer_set.all %} <input type="radio" name="answer" id="answer{{ forloop.counter }}" value="{{ answer.id }}" /> <label for="answer{{ forloop.counter }}"> {{ answer.answer }} </label><br /> {% endfor %} <input type="submit" value="Голосовать" /> </form> {% endif %} {% else %} <p>Извините, опрос снят с публикации.</p> {% endif %}
Проверьте теперь работу приложения опросов. Вам не удастся дважды проголосовать за один и тот же опрос. Чтобы проголосовать еще, вам нужно удалить в админке соответствующие записи модели User.
Комментариев нет:
Отправить комментарий