Author: adrian Date: 2010-01-10 13:23:42 -0600 (Sun, 10 Jan 2010) New Revision: 12194
Modified: django/trunk/django/forms/models.py django/trunk/docs/topics/forms/modelforms.txt django/trunk/tests/modeltests/model_forms/models.py Log: Fixed #9223 -- Added support for declarative widgets to ModelForm. I declare thanks to isagalaev. Modified: django/trunk/django/forms/models.py =================================================================== --- django/trunk/django/forms/models.py 2010-01-10 19:05:39 UTC (rev 12193) +++ django/trunk/django/forms/models.py 2010-01-10 19:23:42 UTC (rev 12194) @@ -159,7 +159,7 @@ data[f.name] = f.value_from_object(instance) 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. @@ -179,7 +179,11 @@ continue if exclude and f.name in exclude: 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: field_list.append((f.name, formfield)) field_dict = SortedDict(field_list) @@ -192,12 +196,13 @@ self.model = getattr(options, 'model', None) self.fields = getattr(options, 'fields', None) self.exclude = getattr(options, 'exclude', None) + self.widgets = getattr(options, 'widgets', None) class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): formfield_callback = attrs.pop('formfield_callback', - lambda f: f.formfield()) + lambda f, **kwargs: f.formfield(**kwargs)) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: @@ -215,7 +220,7 @@ if opts.model: # If a model is defined, extract form fields from it. 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 # (plus, include all the other declared fields). fields.update(declared_fields) Modified: django/trunk/docs/topics/forms/modelforms.txt =================================================================== --- django/trunk/docs/topics/forms/modelforms.txt 2010-01-10 19:05:39 UTC (rev 12193) +++ django/trunk/docs/topics/forms/modelforms.txt 2010-01-10 19:23:42 UTC (rev 12194) @@ -146,7 +146,7 @@ ``default`` value will be initially selected instead). 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 -------------- @@ -350,31 +350,53 @@ .. _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 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 -``ModelForm`` gives you the flexibility of changing the form field type -for a given model field. You 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. +``ModelForm`` gives you the flexibility of changing the form field type and +widget for a given model field. +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`` field, you could do the following:: - >>> class ArticleForm(ModelForm): - ... pub_date = MyDateFormField() - ... - ... class Meta: - ... model = Article + class ArticleForm(ModelForm): + pub_date = MyDateFormField() + + class Meta: + model = Article -If you want to override a field's default widget, then specify the ``widget`` +If you want to override a field's default label, then specify the ``label`` parameter when declaring the form field:: >>> class ArticleForm(ModelForm): - ... pub_date = DateField(widget=MyDateWidget()) + ... pub_date = DateField(label='Publication date') ... ... class Meta: ... model = Article Modified: django/trunk/tests/modeltests/model_forms/models.py =================================================================== --- django/trunk/tests/modeltests/model_forms/models.py 2010-01-10 19:05:39 UTC (rev 12193) +++ django/trunk/tests/modeltests/model_forms/models.py 2010-01-10 19:23:42 UTC (rev 12194) @@ -287,6 +287,27 @@ >>> CategoryForm.base_fields.keys() ['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. 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
-- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.