Fixed #9223 -- Added support for declarative widgets to ModelForm. I declare thanks to isagalaev.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12194 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
06645cbda7
commit
9bb1fa7251
|
@ -159,7 +159,7 @@ def model_to_dict(instance, fields=None, exclude=None):
|
||||||
data[f.name] = f.value_from_object(instance)
|
data[f.name] = f.value_from_object(instance)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
|
def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
||||||
"""
|
"""
|
||||||
Returns a ``SortedDict`` containing form fields for the given model.
|
Returns a ``SortedDict`` containing form fields for the given model.
|
||||||
|
|
||||||
|
@ -179,7 +179,11 @@ def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda
|
||||||
continue
|
continue
|
||||||
if exclude and f.name in exclude:
|
if exclude and f.name in exclude:
|
||||||
continue
|
continue
|
||||||
formfield = formfield_callback(f)
|
if widgets and f.name in widgets:
|
||||||
|
kwargs = {'widget': widgets[f.name]}
|
||||||
|
else:
|
||||||
|
kwargs = {}
|
||||||
|
formfield = formfield_callback(f, **kwargs)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
field_dict = SortedDict(field_list)
|
field_dict = SortedDict(field_list)
|
||||||
|
@ -192,12 +196,13 @@ class ModelFormOptions(object):
|
||||||
self.model = getattr(options, 'model', None)
|
self.model = getattr(options, 'model', None)
|
||||||
self.fields = getattr(options, 'fields', None)
|
self.fields = getattr(options, 'fields', None)
|
||||||
self.exclude = getattr(options, 'exclude', None)
|
self.exclude = getattr(options, 'exclude', None)
|
||||||
|
self.widgets = getattr(options, 'widgets', None)
|
||||||
|
|
||||||
|
|
||||||
class ModelFormMetaclass(type):
|
class ModelFormMetaclass(type):
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
formfield_callback = attrs.pop('formfield_callback',
|
formfield_callback = attrs.pop('formfield_callback',
|
||||||
lambda f: f.formfield())
|
lambda f, **kwargs: f.formfield(**kwargs))
|
||||||
try:
|
try:
|
||||||
parents = [b for b in bases if issubclass(b, ModelForm)]
|
parents = [b for b in bases if issubclass(b, ModelForm)]
|
||||||
except NameError:
|
except NameError:
|
||||||
|
@ -215,7 +220,7 @@ class ModelFormMetaclass(type):
|
||||||
if opts.model:
|
if opts.model:
|
||||||
# If a model is defined, extract form fields from it.
|
# If a model is defined, extract form fields from it.
|
||||||
fields = fields_for_model(opts.model, opts.fields,
|
fields = fields_for_model(opts.model, opts.fields,
|
||||||
opts.exclude, formfield_callback)
|
opts.exclude, opts.widgets, formfield_callback)
|
||||||
# Override default model fields with any custom declared ones
|
# Override default model fields with any custom declared ones
|
||||||
# (plus, include all the other declared fields).
|
# (plus, include all the other declared fields).
|
||||||
fields.update(declared_fields)
|
fields.update(declared_fields)
|
||||||
|
|
|
@ -146,7 +146,7 @@ In addition, each generated form field has attributes set as follows:
|
||||||
``default`` value will be initially selected instead).
|
``default`` value will be initially selected instead).
|
||||||
|
|
||||||
Finally, note that you can override the form field used for a given model
|
Finally, note that you can override the form field used for a given model
|
||||||
field. See `Overriding the default field types`_ below.
|
field. See `Overriding the default field types or widgets`_ below.
|
||||||
|
|
||||||
A full example
|
A full example
|
||||||
--------------
|
--------------
|
||||||
|
@ -350,31 +350,53 @@ Since the Author model has only 3 fields, 'name', 'title', and
|
||||||
|
|
||||||
.. _section on saving forms: `The save() method`_
|
.. _section on saving forms: `The save() method`_
|
||||||
|
|
||||||
Overriding the default field types
|
Overriding the default field types or widgets
|
||||||
----------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
The default field types, as described in the `Field types`_ table above, are
|
The default field types, as described in the `Field types`_ table above, are
|
||||||
sensible defaults. If you have a ``DateField`` in your model, chances are you'd
|
sensible defaults. If you have a ``DateField`` in your model, chances are you'd
|
||||||
want that to be represented as a ``DateField`` in your form. But
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
``ModelForm`` gives you the flexibility of changing the form field type
|
``ModelForm`` gives you the flexibility of changing the form field type and
|
||||||
for a given model field. You do this by declaratively specifying fields like
|
widget for a given model field.
|
||||||
you would in a regular ``Form``. Declared fields will override the default
|
|
||||||
ones generated by using the ``model`` attribute.
|
To specify a custom widget for a field, use the ``widgets`` attribute of the
|
||||||
|
inner ``Meta`` class. This should be a dictionary mapping field names to widget
|
||||||
|
classes or instances.
|
||||||
|
|
||||||
|
For example, if you want the a ``CharField`` to be represented by a
|
||||||
|
``<textarea>`` instead of its default ``<input type="text">``, you can override
|
||||||
|
the field's widget::
|
||||||
|
|
||||||
|
class AuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
fields = ['name', 'title', 'birth_date']
|
||||||
|
widgets = {
|
||||||
|
'name': Textarea(attrs={'cols': 80, 'rows': 20}),
|
||||||
|
}
|
||||||
|
|
||||||
|
The ``widgets`` dictionary accepts either widget instances (e.g.,
|
||||||
|
``Textarea(...)``) or classes (e.g., ``Textarea``).
|
||||||
|
|
||||||
|
If you want to further customize a field -- including its type, label, etc. --
|
||||||
|
you can do this by declaratively specifying fields like you would in a regular
|
||||||
|
``Form``. Declared fields will override the default ones generated by using the
|
||||||
|
``model`` attribute.
|
||||||
|
|
||||||
For example, if you wanted to use ``MyDateFormField`` for the ``pub_date``
|
For example, if you wanted to use ``MyDateFormField`` for the ``pub_date``
|
||||||
field, you could do the following::
|
field, you could do the following::
|
||||||
|
|
||||||
>>> class ArticleForm(ModelForm):
|
class ArticleForm(ModelForm):
|
||||||
... pub_date = MyDateFormField()
|
pub_date = MyDateFormField()
|
||||||
...
|
|
||||||
... class Meta:
|
|
||||||
... model = Article
|
|
||||||
|
|
||||||
If you want to override a field's default widget, then specify the ``widget``
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
|
||||||
|
If you want to override a field's default label, then specify the ``label``
|
||||||
parameter when declaring the form field::
|
parameter when declaring the form field::
|
||||||
|
|
||||||
>>> class ArticleForm(ModelForm):
|
>>> class ArticleForm(ModelForm):
|
||||||
... pub_date = DateField(widget=MyDateWidget())
|
... pub_date = DateField(label='Publication date')
|
||||||
...
|
...
|
||||||
... class Meta:
|
... class Meta:
|
||||||
... model = Article
|
... model = Article
|
||||||
|
|
|
@ -287,6 +287,27 @@ Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh,
|
||||||
>>> CategoryForm.base_fields.keys()
|
>>> CategoryForm.base_fields.keys()
|
||||||
['name']
|
['name']
|
||||||
|
|
||||||
|
Using 'widgets'
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... fields = ['name', 'url', 'slug']
|
||||||
|
... widgets = {
|
||||||
|
... 'name': forms.Textarea,
|
||||||
|
... 'url': forms.TextInput(attrs={'class': 'url'})
|
||||||
|
... }
|
||||||
|
|
||||||
|
>>> str(CategoryForm()['name'])
|
||||||
|
'<textarea id="id_name" rows="10" cols="40" name="name"></textarea>'
|
||||||
|
|
||||||
|
>>> str(CategoryForm()['url'])
|
||||||
|
'<input id="id_url" type="text" class="url" name="url" maxlength="40" />'
|
||||||
|
|
||||||
|
>>> str(CategoryForm()['slug'])
|
||||||
|
'<input id="id_slug" type="text" name="slug" maxlength="20" />'
|
||||||
|
|
||||||
Don't allow more than one 'model' definition in the inheritance hierarchy.
|
Don't allow more than one 'model' definition in the inheritance hierarchy.
|
||||||
Technically, it would generate a valid form, but the fact that the resulting
|
Technically, it would generate a valid form, but the fact that the resulting
|
||||||
save method won't deal with multiple objects is likely to trip up people not
|
save method won't deal with multiple objects is likely to trip up people not
|
||||||
|
|
Loading…
Reference in New Issue