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

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

I am trying to request.user for a form’s clean method, but how can I access the request object? Can I modify the clean method to allow variables input?


回答 0

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)

The answer by Ber – storing it in threadlocals – is a very bad idea. There’s absolutely no reason to do it this way.

A much better way is to override the form’s __init__ method to take an extra keyword argument, request. This stores the request in the form, where it’s required, and from where you can access it in your clean method.

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 ...

and in your view:

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

回答 1

更新时间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 ModelFormWithRequest(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormWithRequest

然后重写MyCustomForm.__init__如下:

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

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

UPDATED 10/25/2011: I’m now using this with a dynamically created class instead of method, as Django 1.3 displays some weirdness otherwise.

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

Then override MyCustomForm.__init__ as follows:

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

You can then access the request object from any method of ModelForm with self.request.


回答 2

值得的是,如果您使用的是基于类的视图,而不是基于函数的视图,请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)

For what it’s worth, if you’re using Class Based Views, instead of function based views, override get_form_kwargs in your editing view. Example code for a custom 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

The above view code will make request available as one of the keyword arguments to the form’s __init__ constructor function. Therefore in your ModelForm do:

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

通常的方法是使用中间件将请求对象存储在线程本地引用中。然后,您可以从应用程序中的任何位置(包括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文档中的说明注册该中间件

The usual aproach is to store the request object in a thread-local reference using a middleware. Then you can access this from anywhere in you app, including the Form.clean() method.

Changing the signature of the Form.clean() method means you have you own, modified version of Django, which may not be what you want.

Thank middleware count look something like this:

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

Register this middleware as described in the Django docs


回答 4

对于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

For Django admin, in 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

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

由于我不想修改视图以将请求作为参数传递给表单,因此我做了以下操作:

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

I ran into this particular problem when customizing the admin. I wanted a certain field to be validated based on the particular admin’s credentials.

Since I did not want to modify the view to pass the request as an argument to the form, the following is what I did:

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

您不能总是使用此方法(及其可能的不良做法),但是如果仅在一个视图中使用表单,则可以将其范围限定在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")

You can’t always use this method (and its probably bad practice), but if you are only using the form in one view you could scope it inside the view method itself.

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

Daniel Roseman的答案仍然是最好的。但是,出于某些原因,我将第一个位置参数用于请求而不是关键字参数:

  1. 您不存在覆盖同名kwarg的风险
  2. 该请求是可选的,不正确。在这种情况下,request属性绝不能为None。
  3. 您可以将args和kwargs干净地传递给父类,而无需修改它们。

最后,我将使用更唯一的名称来避免覆盖现有变量。因此,我修改后的答案如下:

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 ...

The answer by Daniel Roseman is still the best. However, I would use the first positional argument for the request instead of the keyword argument for a few reasons:

  1. You don’t run the risk of overriding a kwarg with the same name
  2. The request is optional which is not right. The request attribute should never be None in this context.
  3. You can cleanly pass the args and kwargs to the parent class without having to modify them.

Lastly, I would use a more unique name to avoid overriding an existing variable. Thus, My modified answer looks like:

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

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

fresh cheese from cheesebaker@pypi: django-requestprovider


回答 9

根据您的要求,我想针对该问题提供另一个答案,您希望将用户访问表单的clean方法。你可以试试看。View.py

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

表格

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中以任何干净的方法访问self.instance

I have another answer to this question as per your requirement you want to access the user into the clean method of the form. You can Try this. 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)

Now you can access the self.instance in any clean method in form.py


回答 10

当您想通过“准备好的” Django类视图访问它时,CreateView有一个小窍门(=官方解决方案无法立即使用)。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

=简而言之,这是通过requestDjango的创建/更新视图传递到表单的解决方案。

When you want to access it through “prepared” Django class views like CreateView there’s a small trick to know (= the official solution doesn’t work out of the box). In your own CreateView you’ll have to add code like this:

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

= in short this is the solution to pass request to your form with Django’s Create/Update views.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。