Answers:
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)
更新时间: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
。
__new__
的kwargs中,稍后将传递给该类的__init__
方法。ModelFormWithRequest
我认为命名该类的含义比清楚得多ModelFormMetaClass
。
值得的是,如果您使用的是基于类的视图,而不是基于函数的视图,请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)
request
能够get_form_kwargs
自动容纳该对象。
self.get_object
?该CreateView
扩展了SingleObjectMixin
。但是,这是可行还是引发异常取决于您是要创建一个新对象还是更新一个现有对象。即测试两种情况(当然也要删除)。
通常的方法是使用中间件将请求对象存储在线程本地引用中。然后,您可以从应用程序中的任何位置(包括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文档中的说明注册该中间件
**kwargs
,这意味着您将必须将请求对象传递为MyForm(request.POST, request=request)
。
对于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
自定义管理员时遇到了这个特殊问题。我希望根据特定管理员的凭据来验证某个字段。
由于我不想修改视图以将请求作为参数传递给表单,因此我做了以下操作:
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
obj=obj
不在obj=None
11号线上
'function' object has no attribute 'base_fields'
。但是,较简单的(不带闭包)@François答案可以正常运行。
您不能总是使用此方法(及其可能的不良做法),但是如果仅在一个视图中使用表单,则可以将其范围限定在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")
get_form_class
如果我知道我需要对请求做很多事情,那么我经常使用CBV 方法执行此操作。重复创建类可能会有一些开销,但这只是将其从导入时间移到运行时。
Daniel Roseman的答案仍然是最好的。但是,出于某些原因,我将第一个位置参数用于请求而不是关键字参数:
最后,我将使用更唯一的名称来避免覆盖现有变量。因此,我修改后的答案如下:
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 ...
来自cheesebaker @ pypi的新鲜奶酪:django-requestprovider
根据您的要求,我想针对该问题提供另一个答案,您希望将用户访问表单的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
当您想通过“准备好的” 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的创建/更新视图传递到表单的解决方案。