Pao*_*ino 145 python forms django django-forms
这是使用form_kwargs在Django 1.9中修复的.
我有一个看起来像这样的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)
Run Code Online (Sandbox Code Playgroud)
我用这样的方式称这个形式:
form = ServiceForm(affiliate=request.affiliate)
Run Code Online (Sandbox Code Playgroud)
request.affiliate登录用户在哪里.这按预期工作.
我的问题是我现在想把这个单一的表单变成一个formset.我无法弄清楚的是,在创建formset时,我如何将联盟信息传递给各个表单.根据文档制作一个formset,我需要做这样的事情:
ServiceFormSet = forms.formsets.formset_factory(ServiceForm, extra=3)
Run Code Online (Sandbox Code Playgroud)
然后我需要像这样创建它:
formset = ServiceFormSet()
Run Code Online (Sandbox Code Playgroud)
现在,我如何通过这种方式将affiliate = request.affiliate传递给单个表单?
Car*_*yer 105
我会使用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)
Run Code Online (Sandbox Code Playgroud)
我认为这是最干净的方法,并且不会以任何方式影响ServiceForm(即难以进行子类化).
roa*_*ach 61
Django 2.0:
ArticleFormSet = formset_factory(MyArticleForm)
formset = ArticleFormSet(form_kwargs={'user': request.user})
Run Code Online (Sandbox Code Playgroud)
Mat*_*all 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
Run Code Online (Sandbox Code Playgroud)
作为奖励,您不必在选项字段中重写查询集.缺点是子类化有点时髦.(任何子类都必须以类似的方式进行.)
编辑:
在回复评论时,您可以在任何使用类名的地方调用此函数:
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)
...
Run Code Online (Sandbox Code Playgroud)
rix*_*rix 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):
Run Code Online (Sandbox Code Playgroud)
希望它对某人有所帮助,带我走了很长时间才弄明白;)
我喜欢封闭解决方案是"更干净"和更Pythonic(所以+1到mmarshall答案)但Django表单也有一个回调机制,你可以用来过滤formsets中的查询集.
它也没有记录,我认为这是Django开发者可能不喜欢它的一个指标.
所以你基本上创建你的formset相同,但添加回调:
ServiceFormSet = forms.formsets.formset_factory(
ServiceForm, extra=3, formfield_callback=Callback('option', affiliate).cb)
Run Code Online (Sandbox Code Playgroud)
这是创建一个类的实例,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
这应该给你一般的想法.使回调成为这样的对象方法有点复杂,但与简单的函数回调相比,它给你一点灵活性.
我想把它作为对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))
Run Code Online (Sandbox Code Playgroud)
截至2012年8月14日23:44:46 + 0200提交时,已接受的解决方案不再适用.
当前版本的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)
Run Code Online (Sandbox Code Playgroud)
这意味着即使是传递的curryed或partial对象而不是表单"导致鸭子咬你"这样说:它将使用ModelFormClass对象的构造参数调用函数,返回错误消息::
function() argument 1 must be code, not str
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,我编写了一个生成器函数,它使用一个闭包来返回指定为第一个参数的任何类的子类,然后在使用生成器函数调用的提供的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
Run Code Online (Sandbox Code Playgroud)
然后在您的代码中,您将表单工厂称为::
MyFormSet = inlineformset_factory(ParentModel, Model,form = class_gen_with_kwarg(MyForm, user=self.request.user))
Run Code Online (Sandbox Code Playgroud)
注意事项:
| 归档时间: |
|
| 查看次数: |
39657 次 |
| 最近记录: |