Существует ли библиотека для предотвращения повторных представлений формы для django?
Я пытаюсь найти способ предотвратить двойное представление пользователями моих форм. У меня есть javascript, который отключает кнопку отправки, но есть еще случайный пользователь, который находит способ удвоить отправку.
У меня есть вид на многократно используемую библиотеку, которую я мог бы создать для защиты от этого.
В моей идеальной библиотеке блок кода будет выглядеть примерно так:
try:
with acquire_lock({'field1':'abc', 'field2':'def'}) as lock:
response = #do some credit card processing
lock.response = response
except SubmissionWasDuplicate, e:
response = e.response
Стол блокировки будет выглядеть примерно так:
duplicate_submission_locks
- submit_hash # MD5 представленных аргументов
- ответ # маринованные данные
- created_at #, используемый для подметания этой таблицы
- lock_expired # boolean, означающий, что срок действия блокировки истек.
Кто-нибудь знает, если это уже существует? Это, кажется, трудно писать, поэтому, если этого не существует, я могу написать его сам.
Ответы
Ответ 1
Одним из простых решений этой проблемы является добавление уникального хэша в каждую форму. Затем вы можете иметь таблицу текущих форм. Когда форма отправляется или хеш становится слишком старым, вы можете закончить его из своей таблицы и отклонить любую форму, которая не имеет соответствующего хэша в вашей таблице.
HTTPRedirect - это правильный способ сделать это, как упоминалось ранее.
К сожалению, даже встроенный администратор Django подвержен проблемам, связанным с этой проблемой. В некоторых случаях межсайтовая структура сценариев может помочь предотвратить некоторые из этих проблем, но я боюсь, что в нынешних производственных версиях этого встроенного устройства нет.
Ответ 2
Вы можете использовать сеанс для хранения хэша
import hashlib
def contact(request):
if request.method == 'POST':
form = MyForm(request.POST)
#join all the fields in one string
hashstring=hashlib.sha1(fieldsstring)
if request.session.get('sesionform')!=hashstring:
if form.is_valid() :
request.session['sesionform'] = hashstring
#do some stuff...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else
raise SubmissionWasDuplicate("duplicate")
else:
form = MyForm()
При таком подходе (не удаляя файл cookie сеанса) пользователь не может повторно хранить данные, используя сеанс, и, кстати, я предполагаю, что существует что-то, кто идентифицирует пользователя, который отправляет данные.
Ответ 3
Честно говоря, ваш лучший выбор (простая и эффективная практика) заключается в том, чтобы выдать страницу HTTPRedirect() на страницу благодарности, и если страница с благодарностью совпадает с формой, это нормально. Вы все еще можете это сделать.
Ответ 4
Ответ Кристиана Дамиана - действительно большое предложение. Я просто подумал о небольшой вариации этой темы, но у нее может быть больше накладных расходов.
Вы можете попробовать реализовать что-то, что используется в django-piston для объектов BaseHandler
, который является методом под названием exists()
, который проверяет, находится ли то, что вы отправляете, в базе данных.
Из handler.py (BaseHandler):
def exists(self, **kwargs):
if not self.has_model():
raise NotImplementedError
try:
self.model.objects.get(**kwargs)
return True
except self.model.DoesNotExist:
return False
Итак, предположим, что вместо функции request_exists()
вызывается функция request_exists()
:
if form.is_valid()
if request_exists(request):
# gracefully reject dupe submission
else:
# do stuff to save the request
...
# and ALWAYS redirect after a POST!!
return HttpResponseRedirect('/thanks/')
Ответ 5
Всегда полезно использовать метод redirect-after-post. Это предотвращает случайную повторную отправку формы с помощью функции обновления из браузера. Это также полезно, даже если вы используете хэш-метод. Это потому, что без перенаправления после POST, в случае нажатия кнопки "Назад/Обновить" пользователь увидит сообщение о повторной отправке формы, что может смутить ее.
Если вы выполняете перенаправление GET после каждого POST, то нажатие Back/Refresh не отображает это сообщение wierd (для обычного пользователя). Поэтому для полной защиты используйте Hash + redirect-after-post.