代码之家  ›  专栏  ›  技术社区  ›  Gattster

是否存在防止为Django提交重复表单的库?

  •  18
  • Gattster  · 技术社区  · 14 年前

    我试图找到一种方法来防止用户重复提交我的表单。我有禁用提交按钮的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

    • 提交的参数的MD5
    • 响应腌制数据
    • 创建于用于扫描此表
    • lock_expired布尔值表示锁是否已过期

    有人知道这是否已经存在吗?它似乎不难写,所以如果它不存在,我可以自己写。

    5 回复  |  直到 8 年前
        1
  •  6
  •   Paul McMillan    14 年前

    这个问题的一个简单的解决方案是向每个表单添加一个唯一的哈希。然后您可以拥有一个当前表单的滚动表。当表单被提交或哈希值太旧时,您可以将其从表中过期,并拒绝表中没有匹配哈希值的任何表单。

    正如前面提到的,httpredirect是正确的方法。

    不幸的是,即使是Django自己的内置管理也容易出现与此问题相关的问题。在某些情况下,跨站点脚本框架可以帮助防止这种情况的发生,但恐怕当前的生产版本没有内置这种功能。

        2
  •  10
  •   Kristian Damian    14 年前

    可以使用会话存储哈希

    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
  •  4
  •   orokusaki    14 年前

    老实说,您的最佳选择(简单而良好的实践)是向“谢谢”页发出一个httpredirect(),如果“谢谢”页与表单相同,那就可以了。你仍然可以这样做。

        4
  •  3
  •   jathanism    14 年前

    克里斯蒂安·达米安的回答确实是一个很好的建议。我只是想在这个主题上做些小小的改动,但它可能会有更多的开销。

    您可以尝试实现在 django-piston 对于 BaseHandler 对象,这是一个名为 exists() 检查您提交的内容是否已在数据库中。

    handler.py (基础处理器):

    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() ,而不是方法:

    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
  •  2
  •   Lukasz Korzybski    14 年前

    使用事后重定向方法总是很好的。这可以防止用户使用浏览器中的刷新功能意外地重新提交表单。即使使用哈希方法,它也很有用。这是因为在发帖后没有重定向,如果点击后退/刷新按钮,用户会看到一条关于重新提交表单的问题消息,这可能会使她困惑。

    如果在每次发帖后都进行get重定向,那么回击/refresh不会显示此wierd(对于普通用户)消息。因此,对于完全保护,在发布后使用hash+重定向。