如何在表单的clean()方法中访问请求对象或任何其他变量?

时间:2009-06-29 08:49:46

标签: python django

我正在尝试request.user获取表单的clean方法,但是如何访问请求对象?我可以修改clean方法以允许变量输入吗?

11 个答案:

答案 0 :(得分:144)

Ber的答案 - 将其存储在threadlocals中 - 是一个非常糟糕的主意。绝对没有理由这样做。

更好的方法是覆盖表单的__init__方法以获取额外的关键字参数request。这会将请求存储在表单中,在那里需要它,以及您可以在干净的方法中访问它。

class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyForm, self).__init__(*args, **kwargs)


    def clean(self):
        ... access the request object via self.request ...

并在您看来:

myform = MyForm(request.POST, request=request)

答案 1 :(得分:30)

更新2011年10月25日:我现在使用的是元类而不是方法,因为Django 1.3显示出一些奇怪的效果。

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormMetaClass(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormMetaClass

然后按如下方式覆盖MyCustomForm.__init__

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

然后,您可以使用ModelForm self.request的任何方法访问请求对象。

答案 2 :(得分:26)

如果您使用基于类的视图,而不是基于功能的视图,则可以在编辑视图中覆盖get_form_kwargs。自定义CreateView的示例代码:

from braces.views import LoginRequiredMixin

class MyModelCreateView(LoginRequiredMixin, CreateView):
    template_name = 'example/create.html'
    model = MyModel
    form_class = MyModelForm
    success_message = "%(my_object)s added to your site."

    def get_form_kwargs(self):
        kw = super(MyModelCreateView, self).get_form_kwargs()
        kw['request'] = self.request # the trick!
        return kw

    def form_valid(self):
        # do something

上面的视图代码将request作为表单的__init__构造函数的关键字参数之一。因此,在ModelForm执行:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        # important to "pop" added kwarg before call to parent's constructor
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)

答案 3 :(得分:16)

通常的方法是使用中间件将请求对象存储在线程局部引用中。然后,您可以从应用程序的任何位置访问它,包括Form.clean()方法。

更改Form.clean()方法的签名意味着您拥有自己的Django修改版本,这可能不是您想要的。

感谢中间件计数看起来像这样:

import threading
_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class ThreadLocals(object):
    """
    Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.request = request

按照Django docs

中的说明注册此中间件

答案 4 :(得分:11)

对于Django管理员,在Django 1.8中

class MyModelAdmin(admin.ModelAdmin):
    ...
    form = RedirectForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form

答案 5 :(得分:7)

我在自定义管理员时遇到了这个特殊问题。我希望根据特定管理员的凭据验证某个字段。

由于我不想修改视图以将请求作为参数传递给表单,以下是我的所作所为:

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def clean(self):
        # make use of self.request here

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            a = ModelForm(*args, **kwargs)
            a.request = request
            return a
    return form_wrapper

答案 6 :(得分:5)

您不能总是使用此方法(可能是不好的做法),但如果您只在一个视图中使用该表单,则可以将其范围放在view方法本身中。

def my_view(request):

    class ResetForm(forms.Form):
        password = forms.CharField(required=True, widget=forms.PasswordInput())

        def clean_password(self):
            data = self.cleaned_data['password']
            if not request.user.check_password(data):
                raise forms.ValidationError("The password entered does not match your account password.")
            return data

    if request.method == 'POST':
        form = ResetForm(request.POST, request.FILES)
        if form.is_valid():

            return HttpResponseRedirect("/")
    else:
        form = ResetForm()

    return render_to_response(request, "reset.html")

答案 7 :(得分:5)

Daniel Roseman的答案仍然是最好的。但是,我会使用请求的第一个位置参数而不是关键字参数,原因如下:

  1. 您不会冒着覆盖同名
  2. 的小丑的风险
  3. 请求是可选的,这是不对的。在此上下文中,request属性永远不应为None。
  4. 您可以干净地将args和kwargs传递给父类,而无需修改它们。
  5. 最后,我会使用一个更独特的名称来避免覆盖现有变量。因此,我修改后的答案如下:

    class MyForm(forms.Form):
    
      def __init__(self, request, *args, **kwargs):
          self._my_request = request
          super(MyForm, self).__init__(*args, **kwargs)
    
    
      def clean(self):
          ... access the request object via self._my_request ...
    

答案 8 :(得分:3)

来自cheesebaker @pypi的新鲜奶酪:django-requestprovider

答案 9 :(得分:3)

我根据您的要求对此问题有另一个答案,您希望将用户访问到表单的clean方法。 你可以试试这个。 View.py

person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)

forms.py

def __init__(self,*arg,**kwargs):
    self.instance=kwargs.get('instance',None)
    if kwargs['instance'] is not None:
        del kwargs['instance']
    super(Myform, self).__init__(*args, **kwargs)

现在,您可以在form.py

中的任何clean方法中访问self.instance

答案 10 :(得分:0)

当您想通过CreateView之类的“准备好的” Django类视图访问它时,有一个小窍门要知道(=官方解决方案无法立即使用)。您必须在自己的CreateView中添加如下代码:

class MyCreateView(LoginRequiredMixin, CreateView):
    form_class = MyOwnForm
    template_name = 'my_sample_create.html'

    def get_form_kwargs(self):
        result = super().get_form_kwargs()
        result['request'] = self.request
        return result

=简而言之,这是使用Django的“创建/更新”视图将request传递到表单的解决方案。