diff --git a/django/forms/models.py b/django/forms/models.py
a
|
b
|
|
150 | 150 | data[f.name] = f.value_from_object(instance) |
151 | 151 | return data |
152 | 152 | |
153 | | def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): |
| 153 | def fields_for_model(model, fields=None, exclude=None, widgets=None, fields_kwargs=None, |
| 154 | formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): |
154 | 155 | """ |
155 | 156 | Returns a ``SortedDict`` containing form fields for the given model. |
156 | 157 | |
… |
… |
|
171 | 172 | continue |
172 | 173 | if exclude and f.name in exclude: |
173 | 174 | continue |
174 | | if widgets and f.name in widgets: |
175 | | kwargs = {'widget': widgets[f.name]} |
| 175 | if fields_kwargs and f.name in fields_kwargs: |
| 176 | kwargs = fields_kwargs[f.name].copy() |
176 | 177 | else: |
177 | 178 | kwargs = {} |
| 179 | if widgets and f.name in widgets: |
| 180 | kwargs['widget'] = widgets[f.name] |
178 | 181 | formfield = formfield_callback(f, **kwargs) |
179 | 182 | if formfield: |
180 | 183 | field_list.append((f.name, formfield)) |
… |
… |
|
194 | 197 | self.fields = getattr(options, 'fields', None) |
195 | 198 | self.exclude = getattr(options, 'exclude', None) |
196 | 199 | self.widgets = getattr(options, 'widgets', None) |
| 200 | self.fields_kwargs = getattr(options, 'fields_kwargs', None) |
197 | 201 | |
198 | 202 | |
199 | 203 | class ModelFormMetaclass(type): |
… |
… |
|
216 | 220 | opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) |
217 | 221 | if opts.model: |
218 | 222 | # If a model is defined, extract form fields from it. |
219 | | fields = fields_for_model(opts.model, opts.fields, |
220 | | opts.exclude, opts.widgets, formfield_callback) |
| 223 | fields = fields_for_model(opts.model, opts.fields, opts.exclude, |
| 224 | opts.widgets, opts.fields_kwargs, formfield_callback) |
221 | 225 | # Override default model fields with any custom declared ones |
222 | 226 | # (plus, include all the other declared fields). |
223 | 227 | fields.update(declared_fields) |
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
a
|
b
|
|
322 | 322 | >>> CategoryForm.base_fields.keys() |
323 | 323 | ['name'] |
324 | 324 | |
| 325 | Using 'fields_kwargs' |
| 326 | |
| 327 | >>> class CategoryForm(ModelForm): |
| 328 | ... |
| 329 | ... class Meta: |
| 330 | ... model = Category |
| 331 | ... fields = ['name', 'url', 'slug'] |
| 332 | ... fields_kwargs = { |
| 333 | ... 'name': {'label': 'Category name', 'widget': forms.Textarea(attrs={'rows': '5', 'cols': '20'})}, |
| 334 | ... 'url': {'max_length': 30}, |
| 335 | ... 'slug': {'max_length': 10, 'widget': forms.TextInput(attrs={'class': 'slug'})}, |
| 336 | ... } |
| 337 | |
| 338 | >>> print CategoryForm() |
| 339 | <tr><th><label for="id_name">Category name:</label></th><td><textarea id="id_name" rows="5" cols="20" name="name"></textarea></td></tr> |
| 340 | <tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="30" /></td></tr> |
| 341 | <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" class="slug" name="slug" maxlength="10" /></td></tr> |
| 342 | |
325 | 343 | Using 'widgets' |
326 | 344 | |
327 | 345 | >>> class CategoryForm(ModelForm): |
… |
… |
|
343 | 361 | >>> str(CategoryForm()['slug']) |
344 | 362 | '<input id="id_slug" type="text" name="slug" maxlength="20" />' |
345 | 363 | |
| 364 | Using 'fields_kwargs' and 'widgets' at once - 'widgets' have higher priority |
| 365 | |
| 366 | >>> class CategoryForm(ModelForm): |
| 367 | ... |
| 368 | ... class Meta: |
| 369 | ... model = Category |
| 370 | ... fields = ['name', 'url', 'slug'] |
| 371 | ... widgets = { |
| 372 | ... 'name': forms.Textarea, |
| 373 | ... 'url': forms.TextInput(attrs={'class': 'url'}) |
| 374 | ... } |
| 375 | ... fields_kwargs = { |
| 376 | ... 'name': {'label': 'Category name', 'widget': forms.Textarea(attrs={'rows': '5', 'cols': '20'})}, |
| 377 | ... 'url': {'max_length': 30}, |
| 378 | ... 'slug': {'max_length': 10, 'widget': forms.TextInput(attrs={'class': 'slug'})}, |
| 379 | ... } |
| 380 | |
| 381 | >>> str(CategoryForm()['name']) |
| 382 | '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>' |
| 383 | |
| 384 | >>> str(CategoryForm()['url']) |
| 385 | '<input id="id_url" type="text" class="url" name="url" maxlength="30" />' |
| 386 | |
| 387 | >>> str(CategoryForm()['slug']) |
| 388 | '<input id="id_slug" type="text" class="slug" name="slug" maxlength="10" />' |
| 389 | |
346 | 390 | Don't allow more than one 'model' definition in the inheritance hierarchy. |
347 | 391 | Technically, it would generate a valid form, but the fact that the resulting |
348 | 392 | save method won't deal with multiple objects is likely to trip up people not |