В предыдущей части урока по 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.
Комментариев нет:
Отправить комментарий