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.


Reply via email to