如何根据James Bennett的文章“你想要一个动态表单”动态创建一个ModelForm?

时间:2012-06-01 02:38:34

标签: python django django-forms django-views

在James Bennett的文章"So you want a dynamic form"(2008年11月9日)中,他写道,要创建一个动态表单,你可以做这样的事情:

def make_contact_form(user):
    fields = { 'name': forms.CharField(max_length=50),
               'email': forms.EmailField(),
               'message': forms.CharField(widget=forms.Textarea) }
    if not user.is_authenticated():
        fields['captcha'] = CaptchaField()
    return type('ContactForm', (forms.BaseForm,), { 'base_fields': fields })

但是你如何用forms.ModelForm做同样的事情?

到目前为止,我只是在做这样的事情(我无法弄清楚如何将type与内部类'Meta'一起使用)

def make_order_edit_form(include_fields):
    class _OrderEditForm(forms.ModelForm):
        if 'fa_date' in include_fields:
            fa_date = CustomDateTimeField(label="first appointment time")

        class Meta:
            model = Order
            fields = include_fields
            widgets = custom_widgets

    return _OrderEditForm

其中include_fields是我要显示的字段元组。

但即使我写了正确的make_order_edit_form,我如何在views.py中使用它?具体来说,我如何将POST请求和订单实例都传递给它?通常我会做类似

的事情
order = Order.objects.get(pk=pk)

order_form = OrderEditForm(data=request.POST, instance=order)

加分问题:

为什么Bennett会从ContactForm而不是forms.BaseForm创建forms.Form? (我假设这就是为什么这些字段也被称为base_fields。)

1 个答案:

答案 0 :(得分:3)

make_order_edit_form会返回ModelForm类,因此您可以

form_cls = make_order_edit_form(fields)
order_form = form_cls(request.POST, instance=order)

对于红利问题,请查看Form代码:

class Form(BaseForm):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.
    __metaclass__ = DeclarativeFieldsMetaclass

Form有一个自定义的元类DeclarativeFieldsMetaclass,它会自动收集以声明性语法编写的表单字段,如果您在Form中使用type(),它看起来像是(采取Bennett& #39; s例子)

type('ContactForm', (forms.Form,), {
    'name': forms.CharField(max_length=50),
    'email': forms.EmailField(),
    'message': forms.CharField(widget=forms.Textarea)}
# instead of
type('ContactForm', (forms.BaseForm,), { 'base_fields': fields })

更新

使用ModelForm构建type,没有太多不同

def make_order_edit_form(include_fields):
    d = {}
    class Meta:
        model = Order
        fields = include_fields
        widgets = custom_widgets 
    d = {'Meta':Meta}
    if 'fa_date' in include_fields:
        d['fa_date'] = CustomDateTimeField(label="first appointment time")
    return type('OrderEditForm', (forms.ModelForm,), d)