Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/353.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用“后退”按钮后防止重新填充和/或重新提交Django表单 问题_Python_Django_Django Forms_Browser Cache_Back Button - Fatal编程技术网

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和表单验证后,将创建对象,用户将重定向到所创建记录的
    详细视图
  • 一些用户认为他们对输入的数据不满意。他们按下后退按钮
  • CreateView
    生成的HTML从浏览器缓存中获取,并用它们输入的数据重新填充
  • 对用户来说,这就像是一次编辑,所以他们会更改数据并再次提交
  • 结果是2条记录,存在细微差异
我们试过什么?
  • 起初,我认为Django使用的Post-Redirect-Get(PRG)模式应该可以防止这种情况。经过调查,PRG似乎只是为了防止可怕的“您想重新提交表单吗?”对话框。死胡同

  • 点击后退按钮后,所有内容都从缓存中获取,因此我们没有机会通过Django代码与用户交互。为了尝试阻止本地缓存,我们用
    @never\u cache
    修饰了
    CreateView
    。这对我们没有任何帮助,页面仍然是从缓存中检索的

  • 我们在考虑什么? 我们正在考虑使用肮脏的JavaScript技巧,对window.referer执行
    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