Loading django/forms/models.py +49 −7 Original line number Diff line number Diff line Loading @@ -138,7 +138,9 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None): def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a ``SortedDict`` containing form fields for the given model. Loading @@ -149,7 +151,16 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c fields will be excluded from the returned fields, even if they are listed in the ``fields`` argument. ``widgets`` is a dictionary of model field names mapped to a widget ``widgets`` is a dictionary of model field names mapped to a widget. ``localized_fields`` is a list of names of fields which should be localized. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. ``formfield_callback`` is a callable that takes a model field and returns a form field. Loading @@ -170,6 +181,12 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c kwargs['widget'] = widgets[f.name] if localized_fields == ALL_FIELDS or (localized_fields and f.name in localized_fields): kwargs['localize'] = True if labels and f.name in labels: kwargs['label'] = labels[f.name] if help_texts and f.name in help_texts: kwargs['help_text'] = help_texts[f.name] if error_messages and f.name in error_messages: kwargs['error_messages'] = error_messages[f.name] if formfield_callback is None: formfield = f.formfield(**kwargs) Loading Loading @@ -197,6 +214,9 @@ class ModelFormOptions(object): self.exclude = getattr(options, 'exclude', None) self.widgets = getattr(options, 'widgets', None) self.localized_fields = getattr(options, 'localized_fields', None) self.labels = getattr(options, 'labels', None) self.help_texts = getattr(options, 'help_texts', None) self.error_messages = getattr(options, 'error_messages', None) class ModelFormMetaclass(type): Loading Loading @@ -248,7 +268,9 @@ class ModelFormMetaclass(type): opts.fields = None fields = fields_for_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback, opts.localized_fields) opts.widgets, formfield_callback, opts.localized_fields, opts.labels, opts.help_texts, opts.error_messages) # make sure opts.fields doesn't specify an invalid field none_model_fields = [k for k, v in six.iteritems(fields) if not v] Loading Loading @@ -416,7 +438,8 @@ class ModelForm(six.with_metaclass(ModelFormMetaclass, BaseModelForm)): pass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None): formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a ModelForm containing form fields for the given model. Loading @@ -434,6 +457,13 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, ``formfield_callback`` is a callable that takes a model field and returns a form field. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. """ # Create the inner Meta class. FIXME: ideally, we should be able to # construct a ModelForm without creating and passing in a temporary Loading @@ -449,6 +479,12 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, attrs['widgets'] = widgets if localized_fields is not None: attrs['localized_fields'] = localized_fields if labels is not None: attrs['labels'] = labels if help_texts is not None: attrs['help_texts'] = help_texts if error_messages is not None: attrs['error_messages'] = error_messages # If parent form class already has an inner Meta, the Meta we're # creating needs to inherit from the parent's inner meta. Loading Loading @@ -738,7 +774,8 @@ class BaseModelFormSet(BaseFormSet): def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None): widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a FormSet class for the given Django model class. """ Loading @@ -759,7 +796,8 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None, form = modelform_factory(model, form=form, fields=fields, exclude=exclude, formfield_callback=formfield_callback, widgets=widgets, localized_fields=localized_fields) widgets=widgets, localized_fields=localized_fields, labels=labels, help_texts=help_texts, error_messages=error_messages) FormSet = formset_factory(form, formset, extra=extra, max_num=max_num, can_order=can_order, can_delete=can_delete, validate_max=validate_max) Loading Loading @@ -898,7 +936,8 @@ def inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None): widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns an ``InlineFormSet`` for the given kwargs. Loading @@ -922,6 +961,9 @@ def inlineformset_factory(parent_model, model, form=ModelForm, 'widgets': widgets, 'validate_max': validate_max, 'localized_fields': localized_fields, 'labels': labels, 'help_texts': help_texts, 'error_messages': error_messages, } FormSet = modelformset_factory(model, **kwargs) FormSet.fk = fk Loading docs/ref/forms/models.txt +20 −9 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ Model Form Functions .. module:: django.forms.models :synopsis: Django's functions for building model forms and formsets. .. function:: modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None) .. function:: modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns a :class:`~django.forms.ModelForm` class for the given ``model``. You can optionally pass a ``form`` argument to use as a starting point for Loading @@ -20,11 +20,18 @@ Model Form Functions ``widgets`` is a dictionary of model field names mapped to a widget. ``localized_fields`` is a list of names of fields which should be localized. ``formfield_callback`` is a callable that takes a model field and returns a form field. ``localized_fields`` is a list of names of fields which should be localized. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. See :ref:`modelforms-factory` for example usage. .. versionchanged:: 1.6 Loading @@ -35,14 +42,16 @@ Model Form Functions information. Omitting any definition of the fields to use will result in all fields being used, but this behavior is deprecated. The ``localized_fields`` parameter was added. The ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added. .. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None) .. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns a ``FormSet`` class for the given ``model`` class. Arguments ``model``, ``form``, ``fields``, ``exclude``, ``formfield_callback``, ``widgets`` and ``localized_fields`` are all passed through to ``formfield_callback``, ``widgets``, ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` are all passed through to :func:`~django.forms.models.modelform_factory`. Arguments ``formset``, ``extra``, ``max_num``, ``can_order``, Loading @@ -54,9 +63,10 @@ Model Form Functions .. versionchanged:: 1.6 The ``widgets``, ``validate_max`` and ``localized_fields`` parameters were added. The ``widgets``, ``validate_max``, ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added. .. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None) .. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns an ``InlineFormSet`` using :func:`modelformset_factory` with defaults of ``formset=BaseInlineFormSet``, ``can_delete=True``, and Loading @@ -69,4 +79,5 @@ Model Form Functions .. versionchanged:: 1.6 The ``widgets``, ``validate_max`` and ``localized_fields`` parameters were added. The ``widgets``, ``validate_max`` and ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added. docs/releases/1.6.txt +8 −3 Original line number Diff line number Diff line Loading @@ -236,9 +236,14 @@ Minor features .. _`Pillow`: https://pypi.python.org/pypi/Pillow .. _`PIL`: https://pypi.python.org/pypi/PIL * :doc:`ModelForm </topics/forms/modelforms/>` accepts a new Meta option: ``localized_fields``. Fields included in this list will be localized * :class:`~django.forms.ModelForm` accepts several new ``Meta`` options. * Fields included in the ``localized_fields`` list will be localized (by setting ``localize`` on the form field). * The ``labels``, ``help_texts`` and ``error_messages`` options may be used to customize the default fields, see :ref:`modelforms-overriding-default-fields` for details. * The ``choices`` argument to model fields now accepts an iterable of iterables instead of requiring an iterable of lists or tuples. Loading docs/topics/forms/modelforms.txt +44 −15 Original line number Diff line number Diff line Loading @@ -141,7 +141,7 @@ In addition, each generated form field has attributes set as follows: ``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 or widgets`_ below. field. See `Overriding the default fields`_ below. A full example -------------- Loading Loading @@ -388,8 +388,10 @@ include that field. .. _section on saving forms: `The save() method`_ Overriding the default field types or widgets --------------------------------------------- .. _modelforms-overriding-default-fields: Overriding the default fields ----------------------------- 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 Loading Loading @@ -420,38 +422,65 @@ widget:: 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. .. versionadded:: 1.6 The ``labels``, ``help_texts`` and ``error_messages`` options were added. Similarly, you can specify the ``labels``, ``help_texts`` and ``error_messages`` attributes of the inner ``Meta`` class if you want to further customize a field. For example, if you wanted to use ``MyDateFormField`` for the ``pub_date`` For example if you wanted to customize the wording of all user facing strings for the ``name`` field:: class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, } Finally, if you want complete control over of a field -- including its type, validators, 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. Fields declared like this will ignore any customizations in the ``widgets``, ``labels``, ``help_texts``, and ``error_messages`` options declared on ``Meta``. For example, if you wanted to use ``MySlugFormField`` for the ``slug`` field, you could do the following:: from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): pub_date = MyDateFormField() slug = MySlugFormField() class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter'] If you want to override a field's default label, then specify the ``label`` parameter when declaring the form field:: If you want to override a field's default validators, then specify the ``validators`` parameter when declaring the form field:: from django.forms import ModelForm, DateField from myapp.models import Article class ArticleForm(ModelForm): pub_date = DateField(label='Publication date') slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter'] fields = ['pub_date', 'headline', 'content', 'reporter', 'slug'] .. note:: Loading Loading @@ -597,7 +626,7 @@ example by specifying the widgets to be used for a given field:: >>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()}) ... widgets={"title": Textarea()}) The fields to include can be specified using the ``fields`` and ``exclude`` keyword arguments, or the corresponding attributes on the ``ModelForm`` inner Loading tests/model_forms/tests.py +59 −10 Original line number Diff line number Diff line Loading @@ -490,7 +490,7 @@ class ModelFormBaseTest(TestCase): ['slug', 'name']) class TestWidgetForm(forms.ModelForm): class FieldOverridesTroughFormMetaForm(forms.ModelForm): class Meta: model = Category fields = ['name', 'url', 'slug'] Loading @@ -498,25 +498,74 @@ class TestWidgetForm(forms.ModelForm): 'name': forms.Textarea, 'url': forms.TextInput(attrs={'class': 'url'}) } labels = { 'name': 'Title', } help_texts = { 'slug': 'Watch out! Letters, numbers, underscores and hyphens only.', } error_messages = { 'slug': { 'invalid': ( "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!" ) } } class TestFieldOverridesTroughFormMeta(TestCase): def test_widget_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertHTMLEqual( str(form['name']), '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>', ) self.assertHTMLEqual( str(form['url']), '<input id="id_url" type="text" class="url" name="url" maxlength="40" />', ) self.assertHTMLEqual( str(form['slug']), '<input id="id_slug" type="text" name="slug" maxlength="20" />', ) class TestWidgets(TestCase): def test_base_widgets(self): frm = TestWidgetForm() def test_label_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertHTMLEqual( str(frm['name']), '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>' str(form['name'].label_tag()), '<label for="id_name">Title:</label>', ) self.assertHTMLEqual( str(frm['url']), '<input id="id_url" type="text" class="url" name="url" maxlength="40" />' str(form['url'].label_tag()), '<label for="id_url">The URL:</label>', ) self.assertHTMLEqual( str(frm['slug']), '<input id="id_slug" type="text" name="slug" maxlength="20" />' str(form['slug'].label_tag()), '<label for="id_slug">Slug:</label>', ) def test_help_text_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertEqual( form['slug'].help_text, 'Watch out! Letters, numbers, underscores and hyphens only.', ) def test_error_messages_overrides(self): form = FieldOverridesTroughFormMetaForm(data={ 'name': 'Category', 'url': '/category/', 'slug': '!%#*@', }) form.full_clean() error = [ "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!", ] self.assertEqual(form.errors, {'slug': error}) class IncompleteCategoryFormWithFields(forms.ModelForm): """ Loading Loading
django/forms/models.py +49 −7 Original line number Diff line number Diff line Loading @@ -138,7 +138,9 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None): def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a ``SortedDict`` containing form fields for the given model. Loading @@ -149,7 +151,16 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c fields will be excluded from the returned fields, even if they are listed in the ``fields`` argument. ``widgets`` is a dictionary of model field names mapped to a widget ``widgets`` is a dictionary of model field names mapped to a widget. ``localized_fields`` is a list of names of fields which should be localized. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. ``formfield_callback`` is a callable that takes a model field and returns a form field. Loading @@ -170,6 +181,12 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c kwargs['widget'] = widgets[f.name] if localized_fields == ALL_FIELDS or (localized_fields and f.name in localized_fields): kwargs['localize'] = True if labels and f.name in labels: kwargs['label'] = labels[f.name] if help_texts and f.name in help_texts: kwargs['help_text'] = help_texts[f.name] if error_messages and f.name in error_messages: kwargs['error_messages'] = error_messages[f.name] if formfield_callback is None: formfield = f.formfield(**kwargs) Loading Loading @@ -197,6 +214,9 @@ class ModelFormOptions(object): self.exclude = getattr(options, 'exclude', None) self.widgets = getattr(options, 'widgets', None) self.localized_fields = getattr(options, 'localized_fields', None) self.labels = getattr(options, 'labels', None) self.help_texts = getattr(options, 'help_texts', None) self.error_messages = getattr(options, 'error_messages', None) class ModelFormMetaclass(type): Loading Loading @@ -248,7 +268,9 @@ class ModelFormMetaclass(type): opts.fields = None fields = fields_for_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback, opts.localized_fields) opts.widgets, formfield_callback, opts.localized_fields, opts.labels, opts.help_texts, opts.error_messages) # make sure opts.fields doesn't specify an invalid field none_model_fields = [k for k, v in six.iteritems(fields) if not v] Loading Loading @@ -416,7 +438,8 @@ class ModelForm(six.with_metaclass(ModelFormMetaclass, BaseModelForm)): pass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None): formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a ModelForm containing form fields for the given model. Loading @@ -434,6 +457,13 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, ``formfield_callback`` is a callable that takes a model field and returns a form field. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. """ # Create the inner Meta class. FIXME: ideally, we should be able to # construct a ModelForm without creating and passing in a temporary Loading @@ -449,6 +479,12 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, attrs['widgets'] = widgets if localized_fields is not None: attrs['localized_fields'] = localized_fields if labels is not None: attrs['labels'] = labels if help_texts is not None: attrs['help_texts'] = help_texts if error_messages is not None: attrs['error_messages'] = error_messages # If parent form class already has an inner Meta, the Meta we're # creating needs to inherit from the parent's inner meta. Loading Loading @@ -738,7 +774,8 @@ class BaseModelFormSet(BaseFormSet): def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None): widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns a FormSet class for the given Django model class. """ Loading @@ -759,7 +796,8 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None, form = modelform_factory(model, form=form, fields=fields, exclude=exclude, formfield_callback=formfield_callback, widgets=widgets, localized_fields=localized_fields) widgets=widgets, localized_fields=localized_fields, labels=labels, help_texts=help_texts, error_messages=error_messages) FormSet = formset_factory(form, formset, extra=extra, max_num=max_num, can_order=can_order, can_delete=can_delete, validate_max=validate_max) Loading Loading @@ -898,7 +936,8 @@ def inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None): widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ Returns an ``InlineFormSet`` for the given kwargs. Loading @@ -922,6 +961,9 @@ def inlineformset_factory(parent_model, model, form=ModelForm, 'widgets': widgets, 'validate_max': validate_max, 'localized_fields': localized_fields, 'labels': labels, 'help_texts': help_texts, 'error_messages': error_messages, } FormSet = modelformset_factory(model, **kwargs) FormSet.fk = fk Loading
docs/ref/forms/models.txt +20 −9 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ Model Form Functions .. module:: django.forms.models :synopsis: Django's functions for building model forms and formsets. .. function:: modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None) .. function:: modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns a :class:`~django.forms.ModelForm` class for the given ``model``. You can optionally pass a ``form`` argument to use as a starting point for Loading @@ -20,11 +20,18 @@ Model Form Functions ``widgets`` is a dictionary of model field names mapped to a widget. ``localized_fields`` is a list of names of fields which should be localized. ``formfield_callback`` is a callable that takes a model field and returns a form field. ``localized_fields`` is a list of names of fields which should be localized. ``labels`` is a dictionary of model field names mapped to a label. ``help_texts`` is a dictionary of model field names mapped to a help text. ``error_messages`` is a dictionary of model field names mapped to a dictionary of error messages. See :ref:`modelforms-factory` for example usage. .. versionchanged:: 1.6 Loading @@ -35,14 +42,16 @@ Model Form Functions information. Omitting any definition of the fields to use will result in all fields being used, but this behavior is deprecated. The ``localized_fields`` parameter was added. The ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added. .. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None) .. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns a ``FormSet`` class for the given ``model`` class. Arguments ``model``, ``form``, ``fields``, ``exclude``, ``formfield_callback``, ``widgets`` and ``localized_fields`` are all passed through to ``formfield_callback``, ``widgets``, ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` are all passed through to :func:`~django.forms.models.modelform_factory`. Arguments ``formset``, ``extra``, ``max_num``, ``can_order``, Loading @@ -54,9 +63,10 @@ Model Form Functions .. versionchanged:: 1.6 The ``widgets``, ``validate_max`` and ``localized_fields`` parameters were added. The ``widgets``, ``validate_max``, ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added. .. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None) .. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None) Returns an ``InlineFormSet`` using :func:`modelformset_factory` with defaults of ``formset=BaseInlineFormSet``, ``can_delete=True``, and Loading @@ -69,4 +79,5 @@ Model Form Functions .. versionchanged:: 1.6 The ``widgets``, ``validate_max`` and ``localized_fields`` parameters were added. The ``widgets``, ``validate_max`` and ``localized_fields``, ``labels``, ``help_texts``, and ``error_messages`` parameters were added.
docs/releases/1.6.txt +8 −3 Original line number Diff line number Diff line Loading @@ -236,9 +236,14 @@ Minor features .. _`Pillow`: https://pypi.python.org/pypi/Pillow .. _`PIL`: https://pypi.python.org/pypi/PIL * :doc:`ModelForm </topics/forms/modelforms/>` accepts a new Meta option: ``localized_fields``. Fields included in this list will be localized * :class:`~django.forms.ModelForm` accepts several new ``Meta`` options. * Fields included in the ``localized_fields`` list will be localized (by setting ``localize`` on the form field). * The ``labels``, ``help_texts`` and ``error_messages`` options may be used to customize the default fields, see :ref:`modelforms-overriding-default-fields` for details. * The ``choices`` argument to model fields now accepts an iterable of iterables instead of requiring an iterable of lists or tuples. Loading
docs/topics/forms/modelforms.txt +44 −15 Original line number Diff line number Diff line Loading @@ -141,7 +141,7 @@ In addition, each generated form field has attributes set as follows: ``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 or widgets`_ below. field. See `Overriding the default fields`_ below. A full example -------------- Loading Loading @@ -388,8 +388,10 @@ include that field. .. _section on saving forms: `The save() method`_ Overriding the default field types or widgets --------------------------------------------- .. _modelforms-overriding-default-fields: Overriding the default fields ----------------------------- 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 Loading Loading @@ -420,38 +422,65 @@ widget:: 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. .. versionadded:: 1.6 The ``labels``, ``help_texts`` and ``error_messages`` options were added. Similarly, you can specify the ``labels``, ``help_texts`` and ``error_messages`` attributes of the inner ``Meta`` class if you want to further customize a field. For example, if you wanted to use ``MyDateFormField`` for the ``pub_date`` For example if you wanted to customize the wording of all user facing strings for the ``name`` field:: class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, } Finally, if you want complete control over of a field -- including its type, validators, 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. Fields declared like this will ignore any customizations in the ``widgets``, ``labels``, ``help_texts``, and ``error_messages`` options declared on ``Meta``. For example, if you wanted to use ``MySlugFormField`` for the ``slug`` field, you could do the following:: from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): pub_date = MyDateFormField() slug = MySlugFormField() class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter'] If you want to override a field's default label, then specify the ``label`` parameter when declaring the form field:: If you want to override a field's default validators, then specify the ``validators`` parameter when declaring the form field:: from django.forms import ModelForm, DateField from myapp.models import Article class ArticleForm(ModelForm): pub_date = DateField(label='Publication date') slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter'] fields = ['pub_date', 'headline', 'content', 'reporter', 'slug'] .. note:: Loading Loading @@ -597,7 +626,7 @@ example by specifying the widgets to be used for a given field:: >>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()}) ... widgets={"title": Textarea()}) The fields to include can be specified using the ``fields`` and ``exclude`` keyword arguments, or the corresponding attributes on the ``ModelForm`` inner Loading
tests/model_forms/tests.py +59 −10 Original line number Diff line number Diff line Loading @@ -490,7 +490,7 @@ class ModelFormBaseTest(TestCase): ['slug', 'name']) class TestWidgetForm(forms.ModelForm): class FieldOverridesTroughFormMetaForm(forms.ModelForm): class Meta: model = Category fields = ['name', 'url', 'slug'] Loading @@ -498,25 +498,74 @@ class TestWidgetForm(forms.ModelForm): 'name': forms.Textarea, 'url': forms.TextInput(attrs={'class': 'url'}) } labels = { 'name': 'Title', } help_texts = { 'slug': 'Watch out! Letters, numbers, underscores and hyphens only.', } error_messages = { 'slug': { 'invalid': ( "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!" ) } } class TestFieldOverridesTroughFormMeta(TestCase): def test_widget_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertHTMLEqual( str(form['name']), '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>', ) self.assertHTMLEqual( str(form['url']), '<input id="id_url" type="text" class="url" name="url" maxlength="40" />', ) self.assertHTMLEqual( str(form['slug']), '<input id="id_slug" type="text" name="slug" maxlength="20" />', ) class TestWidgets(TestCase): def test_base_widgets(self): frm = TestWidgetForm() def test_label_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertHTMLEqual( str(frm['name']), '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>' str(form['name'].label_tag()), '<label for="id_name">Title:</label>', ) self.assertHTMLEqual( str(frm['url']), '<input id="id_url" type="text" class="url" name="url" maxlength="40" />' str(form['url'].label_tag()), '<label for="id_url">The URL:</label>', ) self.assertHTMLEqual( str(frm['slug']), '<input id="id_slug" type="text" name="slug" maxlength="20" />' str(form['slug'].label_tag()), '<label for="id_slug">Slug:</label>', ) def test_help_text_overrides(self): form = FieldOverridesTroughFormMetaForm() self.assertEqual( form['slug'].help_text, 'Watch out! Letters, numbers, underscores and hyphens only.', ) def test_error_messages_overrides(self): form = FieldOverridesTroughFormMetaForm(data={ 'name': 'Category', 'url': '/category/', 'slug': '!%#*@', }) form.full_clean() error = [ "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!", ] self.assertEqual(form.errors, {'slug': error}) class IncompleteCategoryFormWithFields(forms.ModelForm): """ Loading