【问题标题】:Django: Access request object from admin's form.clean()Django:从管理员的 form.clean() 访问请求对象
【发布时间】:2011-02-10 15:20:22
【问题描述】:

我的问题和这个很相似:How do I access the request object or any other variable in a form's clean() method?

除了,我对管理表单有同样的问题。所以我看不到自己初始化表单的方法,因此 - 将请求传递给它。

先谢谢了。

【问题讨论】:

    标签: django django-admin django-forms


    【解决方案1】:

    确实有办法解决您的问题!

    您将需要 ModelAdmin.get_form() 提供的 subclass form 并覆盖它:

    class BusinessDocumentCommentForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            self.request = kwargs.pop('request', None)
            # Voila, now you can access request anywhere in your form methods by using self.request!
            super(BusinessDocumentCommentForm, self).__init__(*args, **kwargs)
            if self.request.GET.get('document_pk', False):
                #Do something
        def clean(self):
            # Do something with self.request
            # etc.    
        class Meta:
            model = BusinessDocumentComment
    
    class BusinessDocumentCommentAdmin(admin.ModelAdmin):
    
        form = BusinessDocumentCommentForm     
    
        def get_form(self, request, obj=None, **kwargs):
    
            AdminForm = super(BusinessDocumentCommentAdmin, self).get_form(request, obj, **kwargs)
    
            class AdminFormWithRequest(AdminForm):
                def __new__(cls, *args, **kwargs):
                    kwargs['request'] = request
                    return AdminForm(*args, **kwargs)
    
            return AdminFormWithRequest
    

    【讨论】:

    • 这是解决这个问题的好方法!但是,我对您在这里使用“MetaClass”一词感到有些困惑。如我所见, ModelFormMetaClass 只是表单的超类。我在这里错过了什么?
    • @mkoistinen,你是绝对正确的。这个想法在我过去的某个时候出现过,但我懒得纠正答案。由于人们仍在使用sn-p,我有责任纠正它。感谢您的反馈。 :-)
    • 感谢更新,我在上面的评论中弄错了。它是一个子类。糟糕!
    • 所以这个为每次调用 get_form 创建一个新类? ISTR 认为类对象在 Python 进程生命周期内永远不会被释放。如果属实,那么这个解决方案就是内存泄漏……
    • @melbic 我在 3.0 中看到它:docs.djangoproject.com/en/3.0/ref/contrib/admin/…
    【解决方案2】:

    ModelAdmin 类中有许多钩子可让您执行此操作 - 请查看 django.contrib.admin.options 中的代码。

    ModelAdmin.save_formModelAdmin.save_model 这两个方法可能对您有所帮助,这两个方法都传递了请求对象。因此,您可以在 Admin 子类中覆盖这些方法并执行您需要的任何额外处理。

    评论后编辑

    你说得对,这不会让你根据用户的权限来验证表单。不幸的是,表单实例化深埋在ModelAdminadd_viewchange_view 方法中。

    如果不复制大量现有代码,可能性并不大。您可以覆盖 *_view 方法;或者您可以尝试覆盖modelform_factory 函数以返回一个新类,其中已经包含请求对象;或者您可以尝试使用表单类 __new__ 方法来做同样的事情,但由于表单元类,这很棘手。

    【讨论】:

    • 丹尼尔,感谢您的回答!实际上,在实现表单之前,我已经尝试过覆盖这些方法......但是 - 我理解(从你的一个答案中,如果我没记错的话)没有办法从这些方法中执行验证。通过“验证”,我的意思是如果我不喜欢某些内容,则不保存某些内容的能力,同时提供一些有价值的反馈的能力。我的任务是 - 根据用户权限验证管理员输入。可能是我挖错地方了吗?
    • 见我上面的扩展答案。
    【解决方案3】:

    这个解决方案对我有用。你可以在表单的任何地方使用self.request来使用它,包括def clean(self)

    class MyModelAdmin(admin.ModelAdmin):
        form = MyForm
    
        def get_form(self, request, *args, **kwargs):
            form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
            form.request = request
            return form
    

    【讨论】:

      【解决方案4】:

      还有另一种可能性:在您看来,覆盖get_form_kwargs 并添加request

      class FilesUploadCreateView(LoginRequiredMixin, generic.CreateView):
          def get_form_kwargs(self):
              result = super().get_form_kwargs()
              result['request'] = self.request
              return result
      

      现在在你的表单中,当它被创建时,记住它,所以,在这个特定Form 类的所有方法中,你将能够通过self.request 访问请求属性:

      class UploadFilesForm(forms.ModelForm):
          def __init__(self, *args, **kwargs):
              self.request = kwargs.pop('request')
              super().__init__(*args, **kwargs)
      

      现在您可以使用self.request 访问表单中的任何位置!

      【讨论】:

      • OP 已特别提到他无权访问 View,因此无法覆盖 get_form_kwargs,因为他想处理管理面板的更改。
      • 他无权访问视图,因为表单默认情况下无权访问视图。我的方法在表单的kwargs 中添加了一个变量,当它被实例化时,我“弹出”了request 变量。没有任何问题(除非得到证明),我的回答也没有什么不妥,而且它工作得完美无缺。
      • 是的,您的解决方案毫无疑问是正确的!我只是想知道它是如何解决上述问题的。 OP 在描述中提到了另一个问题链接,该解决方案是理想的解决方案,但我怀疑这种情况(管理表单)是否可行。
      最近更新 更多