问题:如何访问表单的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)
然后,您可以使用ModelForm
with的任何方法访问请求对象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的答案仍然是最好的。但是,出于某些原因,我将第一个位置参数用于请求而不是关键字参数:
- 您不存在覆盖同名kwarg的风险
- 该请求是可选的,不正确。在这种情况下,request属性绝不能为None。
- 您可以将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:
- You don’t run the risk of overriding a kwarg with the same name
- The request is optional which is not right. The request attribute should never be None in this context.
- 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
回答 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
=简而言之,这是通过request
Django的创建/更新视图传递到表单的解决方案。
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.
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。