Python 使用“后退”按钮后防止重新填充和/或重新提交Django表单 问题
我们有以下设置Python 使用“后退”按钮后防止重新填充和/或重新提交Django表单 问题,python,django,django-forms,browser-cache,back-button,Python,Django,Django Forms,Browser Cache,Back Button,我们有以下设置 相当标准的基于Django类的视图(继承自CreateView,我现在称之为表单) 成功的POST和表单验证后,将创建对象,用户将重定向到所创建记录的详细视图 一些用户认为他们对输入的数据不满意。他们按下后退按钮 CreateView生成的HTML从浏览器缓存中获取,并用它们输入的数据重新填充 对用户来说,这就像是一次编辑,所以他们会更改数据并再次提交 结果是2条记录,存在细微差异 我们试过什么? 起初,我认为Django使用的Post-Redirect-Get(PRG)模式
- 相当标准的基于Django类的视图(继承自
,我现在称之为表单)CreateView
- 成功的POST和表单验证后,将创建对象,用户将重定向到所创建记录的
详细视图
- 一些用户认为他们对输入的数据不满意。他们按下后退按钮
生成的HTML从浏览器缓存中获取,并用它们输入的数据重新填充CreateView
- 对用户来说,这就像是一次编辑,所以他们会更改数据并再次提交
- 结果是2条记录,存在细微差异
@never\u cache
修饰了CreateView
。这对我们没有任何帮助,页面仍然是从缓存中检索的onLoad
检查,如果referer看起来像前面提到的DetailView
,则手动清理表单和/或通知用户。当然,这感觉完全错了。同样,数据库中的半重复记录也是如此
然而,我们似乎不太可能是第一个被这件事困扰的人,所以我想在这里询问一下StackOverflow
理想情况下,我们会告诉浏览器缓存表单是一个大问题,浏览器会倾听。同样,我们已经使用了@never\u cache
,但显然这还不够。发生在Chrome、Safari和Firefox中
期待任何见解!谢谢 当来自同一页面以外的推荐人时,可能不处理POST请求
from urllib import parse
class CreateView(...):
def post(self, *args, **kwargs):
referer = 'HTTP_REFERER' in self.request.META and parse.urlparse(self.request.META['HTTP_REFERER'])
if referer and (referer.netloc != self.request.META.get('HTTP_HOST') or referer.path != self.request.META.get('PATH_INFO')):
return self.get(*args, **kwargs)
...
我知道我来晚了,但这可能会帮助其他人寻找答案 在为同一个问题绞尽脑汁时发现了这一点,下面是我使用人为因素而非技术因素的解决方案。如果从CreateView提交后,用户在新创建的对象的更新视图中结束,并且除了标题和底部的按钮外,该对象看起来完全相同,则用户不会使用“后退”按钮 一种技术解决方案可能是创建一个模型字段来保存UUID,并创建一个UUID作为隐藏字段传递到create表单中。当按下提交时,
form\u valid
可以在数据库中检查具有该UUID的对象,并拒绝创建重复的对象(unique=True
将在数据库级别强制执行该操作)
下面是示例代码(稍加修改以删除我的雇主在公共场合可能不想要的内容)。它用来使事情变得漂亮和简单。创建视图是通过客户表上的按钮输入的,该按钮传递的是客户帐号,而不是其记录的Django id
网址
投票赞成创造性的想法。然而,这会冒犯用户。因为用户使用缓存中的
CreateView
来输入(大量)新数据,并且在这样的修复之后,数据将消失,甚至可能没有解释。我们正在寻找的是防止缓存的表单出现。
url(r'enter/(?P<customer>[-\w]+)/$', JobEntryView.as_view(), name='job_entry'),
url(r'update1/(?P<pk>\d+)/$', JobEntryUpdateView.as_view(), name='entry_update'),
class JobEntryView( LoginRequiredMixin, CreateView):
model=Job
form_class=JobEntryForm
template_name='utils/generic_crispy_form.html' # basically just {% crispy form %}
def get_form( self, form_class=None):
self.customer = get_object_or_404(
Customer, account = self.kwargs.get('customer','?') )
self.crispy_title = f"Create job for {self.customer.account} ({self.customer.fullname})"
return super().get_form( form_class)
def form_valid( self, form): # insert created_by'class
#form.instance.entered_by = self.request.user
form.instance.customer = self.customer
return super().form_valid(form)
def get_success_url( self):
return reverse( 'jobs:entry_update', kwargs={'pk':self.object.pk, } )
# redirect to this after entry ... user hopefully won't use back because it's here already
class JobEntryUpdateView( LoginRequiredMixin, CrispyCMVPlugin, UpdateView):
model=Job
form_class=JobEntryForm
template_name='utils/generic_crispy_form.html'
def get_form( self, form_class=None):
self.customer = self.object.customer
self.crispy_title = f"Update job {self.object.jobno} for {self.object.customer.account} ({self.object.customer.fullname})"
form = super().get_form( form_class)
form.helper[-1] = ButtonHolder( Submit('update', 'Update', ), Submit('done', 'Done', ), )
return form
def get_success_url( self):
print( self.request.POST )
if self.request.POST.get('done',None):
return reverse('jobs:ok')
return reverse( 'jobs:entry_update',
kwargs={'pk':self.object.pk, } ) # loop until user clicks Done