Reputation: 30308
In James Bennett's article "So you want a dynamic form" (Nov. 9, 2008), he wrote that to create a dynamic form you can do something like this:
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 })
But how would you do the same thing with forms.ModelForm
?
So far I'm just doing something like this (I couldn't figure out how to use type
with an inner class '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
where include_fields
is a tuple of fields I want to show.
However even if I wrote a correct make_order_edit_form
, how do I use it in views.py
? Specifically how do I pass both the POST request and the order instance to it? Normally I would do something like
order = Order.objects.get(pk=pk)
order_form = OrderEditForm(data=request.POST, instance=order)
Bonus question:
Why did Bennett create a ContactForm
out of forms.BaseForm
instead of forms.Form
? (I'm assuming that's why the fields are called base_fields
as well.)
Upvotes: 2
Views: 217
Reputation: 23871
make_order_edit_form
returns a ModelForm
class, thus you could
form_cls = make_order_edit_form(fields)
order_form = form_cls(request.POST, instance=order)
For the bonus question, check the Form
code:
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
The Form
has a customized meta class DeclarativeFieldsMetaclass
which automatically collects form fields written in declarative syntax, if you use Form
in type()
, it looks like (take Bennett's example)
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 })
update
to build up ModelForm
using type
, no much different
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)
Upvotes: 3