这已在Django 1.9中使用form_kwargs修复。
我有一个看起来像这样的Django表单:
class ServiceForm(forms.Form):
option = forms.ModelChoiceField(queryset=ServiceOption.objects.none())
rate = forms.DecimalField(widget=custom_widgets.SmallField())
units = forms.IntegerField(min_value=1, widget=custom_widgets.SmallField())
def __init__(self, *args, **kwargs):
affiliate = kwargs.pop('affiliate')
super(ServiceForm, self).__init__(*args, **kwargs)
self.fields["option"].queryset = ServiceOption.objects.filter(affiliate=affiliate)
我用这样的方式称这个形式:
form = ServiceForm(affiliate=request.affiliate)
登录用户request.affiliate
的位置。这可以按预期工作。
我的问题是我现在想把这个单一形式变成一个formset。我无法弄清楚的是,在创建formset时,我如何将联盟信息传递给各个表单。根据文档制作一个表格,我需要做这样的事情:
ServiceFormSet = forms.formsets.formset_factory(ServiceForm, extra=3)
然后我需要像这样创建它:
formset = ServiceFormSet()
现在我怎么能以这种方式将affiliate = request.affiliate传递给各个表单?
答案 0 :(得分:104)
我会使用functools.partial和functools.wraps:
from functools import partial, wraps
from django.forms.formsets import formset_factory
ServiceFormSet = formset_factory(wraps(ServiceForm)(partial(ServiceForm, affiliate=request.affiliate)), extra=3)
我认为这是最干净的方法,并且不会以任何方式影响ServiceForm(即难以进行子类化)。
答案 1 :(得分:47)
Django 2.0:
ArticleFormSet = formset_factory(MyArticleForm)
formset = ArticleFormSet(form_kwargs={'user': request.user})
答案 2 :(得分:45)
我会在一个函数中动态构建表单类,以便它可以通过闭包访问联盟:
def make_service_form(affiliate):
class ServiceForm(forms.Form):
option = forms.ModelChoiceField(
queryset=ServiceOption.objects.filter(affiliate=affiliate))
rate = forms.DecimalField(widget=custom_widgets.SmallField())
units = forms.IntegerField(min_value=1,
widget=custom_widgets.SmallField())
return ServiceForm
作为奖励,您不必在选项字段中重写查询集。缺点是子类化有点时髦。 (任何子类都必须以类似的方式进行。)
修改强>
在回复评论时,您可以调用此函数关于您将使用类名的任何地方:
def view(request):
affiliate = get_object_or_404(id=request.GET.get('id'))
formset_cls = formset_factory(make_service_form(affiliate))
formset = formset_cls(request.POST)
...
答案 3 :(得分:16)
这对我有用,Django 1.7:
from django.utils.functional import curry
lols = {'lols':'lols'}
formset = modelformset_factory(MyModel, form=myForm, extra=0)
formset.form = staticmethod(curry(MyForm, lols=lols))
return formset
#form.py
class MyForm(forms.ModelForm):
def __init__(self, lols, *args, **kwargs):
希望它对某人有所帮助,让我花了很长时间才弄明白;)
答案 4 :(得分:9)
我喜欢闭包解决方案,因为它更“干净”,更像Pythonic(所以+1来回答),但是Django表单也有一个回调机制,可用于过滤表单集中的查询集。
它也没有记录,我认为这是一个指示Django开发者可能不喜欢它。
所以你基本上创建了你的formset,但添加了回调:
ServiceFormSet = forms.formsets.formset_factory(
ServiceForm, extra=3, formfield_callback=Callback('option', affiliate).cb)
这是创建一个类的实例,如下所示:
class Callback(object):
def __init__(self, field_name, aff):
self._field_name = field_name
self._aff = aff
def cb(self, field, **kwargs):
nf = field.formfield(**kwargs)
if field.name == self._field_name: # this is 'options' field
nf.queryset = ServiceOption.objects.filter(affiliate=self._aff)
return nf
这应该给你一般的想法。使回调成为这样的对象方法有点复杂,但与简单的函数回调相比,它给你一点灵活性。
答案 5 :(得分:9)
我想将此作为对Carl Meyers答案的评论,但由于这需要点我才把它放在这里。这花了我2个小时才弄清楚,所以我希望它会帮助别人。
关于使用inlineformset_factory的说明。
我自己使用了这个解决方案并且它工作得很完美,直到我用inlineformset_factory尝试它。我正在运行Django 1.0.2并得到一些奇怪的KeyError异常。我升级到最新的主干,它直接工作。
我现在可以使用它类似于:
BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
BookFormSet.form = staticmethod(curry(BookForm, user=request.user))
答案 6 :(得分:9)
自2012年8月14日星期二23:44:46 + 0200提交e091c18f50266097f648efc7cac2503968e9d217后,已接受的解决方案不再适用。
当前版本的django.forms.models.modelform_factory()函数使用“类型构造技术”,在传递的表单上调用type()函数以获取元类类型,然后使用结果构造类 - 动态类型的对象::
# Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs)
这意味着即使传递curry
ed或partial
对象而不是表单“会导致鸭子咬你”可以这么说:它会调用一个带有{的构造参数的函数{1}}对象,返回错误消息::
ModelFormClass
为了解决这个问题,我编写了一个生成器函数,它使用一个闭包来返回指定为第一个参数的任何类的子类,然后在function() argument 1 must be code, not str
kwargs之后调用super.__init__
。生成器函数的调用::
update
然后在您的代码中,您将表单工厂称为::
def class_gen_with_kwarg(cls, **additionalkwargs):
"""class generator for subclasses with additional 'stored' parameters (in a closure)
This is required to use a formset_factory with a form that need additional
initialization parameters (see http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset)
"""
class ClassWithKwargs(cls):
def __init__(self, *args, **kwargs):
kwargs.update(additionalkwargs)
super(ClassWithKwargs, self).__init__(*args, **kwargs)
return ClassWithKwargs
注意事项:
答案 7 :(得分:3)
Carl Meyer的解决方案看起来非常优雅。我尝试为modelformsets实现它。我的印象是我不能在类中调用static方法,但以下莫名其妙地工作:
class MyModel(models.Model):
myField = models.CharField(max_length=10)
class MyForm(ModelForm):
_request = None
class Meta:
model = MyModel
def __init__(self,*args,**kwargs):
self._request = kwargs.pop('request', None)
super(MyForm,self).__init__(*args,**kwargs)
class MyFormsetBase(BaseModelFormSet):
_request = None
def __init__(self,*args,**kwargs):
self._request = kwargs.pop('request', None)
subFormClass = self.form
self.form = curry(subFormClass,request=self._request)
super(MyFormsetBase,self).__init__(*args,**kwargs)
MyFormset = modelformset_factory(MyModel,formset=MyFormsetBase,extra=1,max_num=10,can_delete=True)
MyFormset.form = staticmethod(curry(MyForm,request=MyFormsetBase._request))
在我看来,如果我这样做:
formset = MyFormset(request.POST,queryset=MyModel.objects.all(),request=request)
然后“request”关键字传播到我的formset的所有成员表单。我很高兴,但我不知道为什么这样做 - 这似乎不对。有什么建议吗?
答案 8 :(得分:1)
在看到这个帖子之前,我花了一些时间试图找出这个问题。
我提出的解决方案是闭包解决方案(这是我之前使用Django模型表单的解决方案)。
我尝试了如上所述的curry()方法,但我无法让它与Django 1.0一起工作,所以最后我恢复了闭包方法。
闭包方法非常简洁,唯一有点奇怪的是类定义嵌套在视图或其他函数中。我觉得这对我来说很奇怪,这是我以前的编程经验中的一个问题,我认为有更多动态语言背景的人不会眨眼睛!
答案 9 :(得分:1)
我不得不做类似的事情。这类似于curry
解决方案:
def form_with_my_variable(myvar):
class MyForm(ServiceForm):
def __init__(self, myvar=myvar, *args, **kwargs):
super(SeriveForm, self).__init__(myvar=myvar, *args, **kwargs)
return MyForm
factory = inlineformset_factory(..., form=form_with_my_variable(myvar), ... )
答案 10 :(得分:0)
我是新手,所以我无法添加评论。我希望这段代码也能正常工作:
ServiceFormSet = formset_factory(ServiceForm, extra=3)
ServiceFormSet.formset = staticmethod(curry(ServiceForm, affiliate=request.affiliate))
将其他参数添加到formset的BaseFormSet
而不是表单。
答案 11 :(得分:0)
基于this answer我找到了更明确的解决方案:
class ServiceForm(forms.Form):
option = forms.ModelChoiceField(
queryset=ServiceOption.objects.filter(affiliate=self.affiliate))
rate = forms.DecimalField(widget=custom_widgets.SmallField())
units = forms.IntegerField(min_value=1,
widget=custom_widgets.SmallField())
@staticmethod
def make_service_form(affiliate):
self.affiliate = affiliate
return ServiceForm
并在视图中运行
formset_factory(form=ServiceForm.make_service_form(affiliate))