Forms ----- .. _Field.parse: How do I supply a custom parser for a field? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pass a callable to the `parse` member of the field: .. code-block:: python form = Form( auto__model=Track, fields__index__parse=lambda field, string_value, **_: int(string_value[:-3]), ) .. _Field.editable: How do I make a field non-editable? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two cases: A) non-editable and you want to show the value to the user, or B) non-editable but do not show it ("hardcoded"). A) Show the value ================= Pass a callable or `bool` to the `editable` member of the field: .. code-block:: python form = Form( auto__model=Album, fields__name__editable=lambda request, **_: request.user.is_staff, fields__artist__editable=False, ) For a normal user: .. raw:: html
▼ Hide result
For a staff user: .. raw:: html
▼ Hide result
B) Hardcode the value ===================== A common use case is to navigate to some object, then create a sub-object. In this example we have a url like `/artists/Black Sabbath/`, where the artist name is parsed into an `Artist` instance by an iommi path decoder. Then under that we have `/artists/Black Sabbath/create_album/`, and in this form, we don't want to make the user choose Black Sabbath again. We accomplish this with the `hardcoded` shortcut: .. code-block:: python form = Form.create( auto__model=Album, fields__artist=Field.hardcoded( parsed_data=lambda params, **_: params.artist, ), ) .. raw:: html
▼ Hide result
.. _Form.editable: How do I make an entire form non-editable? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a very common case so there's a special syntax for this: pass a `bool` to the form: .. code-block:: python form = Form.edit( auto__instance=album, editable=False, ) .. raw:: html
▼ Hide result
.. _Field.is_valid: How do I supply a custom validator? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pass a callable that has the arguments `form`, `field`, and `parsed_data`. Return a tuple `(is_valid, 'error message if not valid')`. .. code-block:: python form = Form.create( auto__model=Album, auto__include=['name'], fields__name__is_valid=lambda form, field, parsed_data, **_: ( parsed_data == 'only this value is valid', 'invalid!', ), ) .. raw:: html
▼ Hide result
You can also raise `ValidationError`: .. code-block:: python def name_is_valid(form, field, parsed_data, **_): if parsed_data != 'only this value is valid': raise ValidationError('invalid!') form = Form.create( auto__model=Album, auto__include=['name'], fields__name__is_valid=name_is_valid, ) .. raw:: html
▼ Hide result
How do I validate multiple fields together? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Refine the `post_validation` hook on the `form`. It is run after all the individual fields validation has run. But note that it is run even if the individual fields validation was not successful. How do I exclude a field? ~~~~~~~~~~~~~~~~~~~~~~~~~ See `How do I say which fields to include when creating a form from a model?`_ How do I say which fields to include when creating a form from a model? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `Form()` has four methods to select which fields are included in the final form: 1. the `auto__include` parameter: this is a list of strings for members of the model to use to generate the form. 2. the `auto__exclude` parameter: the inverse of `include`. If you use this the form gets all the fields from the model excluding the ones with names you supply in `exclude`. 3. for more advanced usages you can also pass the `include` parameter to a specific field like `fields__my_field__include=True`. Here you can supply either a `bool` or a callable like `fields__my_field__include=lambda request, **_: request.user.is_staff`. 4. you can also add fields that are not present in the model by passing configuration like `fields__foo__attr='bar__baz'` (this means create a `Field` called `foo` that reads its data from `bar.baz`). You can either pass configuration data like that, or pass an entire `Field` instance. .. _Field.initial: How do I supply a custom initial value? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pass a value or callable to the `initial` member: .. code-block:: python form = Form( auto__model=Album, fields__name__initial='Paranoid', fields__year__initial=lambda field, form, **_: 1970, ) .. raw:: html
▼ Hide result
If there are `GET` parameters in the request, iommi will use them to fill in the appropriate fields. This is very handy for supplying links with partially filled in forms from just a link on another part of the site. .. _Field.required: How do I set if a field is required? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Normally this will be handled automatically by looking at the model definition, but sometimes you want a form to be more strict than the model. Pass a `bool` or a callable to the `required` member: .. code-block:: python form = Form.create( auto__model=Album, fields__name__required=True, fields__year__required=lambda field, form, **_: True, ) .. raw:: html
▼ Hide result
To show the field as required before posting, you can add a CSS class rendering to your style definition: .. code-block:: python IOMMI_DEFAULT_STYLE = Style( bootstrap, Field__attrs__class__required=lambda field, **_: field.required, ) ...and this CSS added to your sites custom style sheet: .. code-block:: css .required label:after { content: " *"; color: red; } For the following result: .. raw:: html
▼ Hide result
See the style docs for more information on defining a custom style for your project. .. _Field.after: How do I change the order of the fields? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can change the order in your model definitions as this is what iommi uses. If that's not practical you can use the `after` member. It's either the name of a field or an index. There is a special value `LAST` to put a field last. .. code-block:: python from iommi import LAST form = Form( auto__model=Album, fields__name__after=LAST, fields__year__after='artist', fields__artist__after=0, ) .. raw:: html
▼ Hide result
This will make the field order `artist`, `year`, `name`. If there are multiple fields with the same index or name the order of the fields will be used to disambiguate. .. _Field.search_fields: How do I specify which model fields the search of a choice_queryset use? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `Form.choice_queryset` uses the registered search fields for filtering and ordering. See :doc:`registrations` for how to register one. If present it will default to a model field `name`. In special cases you can override which attributes it uses for searching by specifying `search_fields`: .. code-block:: python form = Form( auto__model=Album, fields__name__search_fields=('name', 'year'), ) This last method is discouraged though, because it will mean searching behaves differently in different parts of your application for the same data. How do I insert a CSS class or HTML attribute? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See :doc:`Attrs`. .. _Field.template: How do I override rendering of an entire field? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pass a template name: .. code-block:: python form = Form( auto__model=Album, fields__year__template='my_template.html', ) .. raw:: html
▼ Hide result
or a `Template` object: .. code-block:: python form = Form( auto__model=Album, fields__year__template=Template('This is from the inline template'), ) .. raw:: html
▼ Hide result
.. _Field.input: How do I override rendering of the input field? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pass a template name or a `Template` object to the `input` namespace: .. code-block:: python form = Form( auto__model=Album, fields__year__input__template='my_template.html', ) .. raw:: html
▼ Hide result
.. code-block:: python form = Form( auto__model=Album, fields__year__input__template=Template('This is from the inline template'), ) .. raw:: html
▼ Hide result
How do I change how fields are rendered everywhere in my project? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Define a custom style and override the appropriate fields. For example here is how you could change `Field.date` to use a text based input control (as opposed to the date picker that `input type='date'` uses). .. code-block:: python my_style = Style(bootstrap, Field__shortcuts__date__input__attrs__type='text') When you do that you will get English language relative date parsing (e.g. "yesterday", "3 days ago") for free, because iommi used to use a text based input control and the parser is applied no matter what (its just that when using the default date picker control it will always only see ISO-8601 dates). .. raw:: html
▼ Hide result
How do I change where the form redirects to after completion? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ iommi by default redirects to `..` after edit/create/delete. You can override this via two methods: - `extra__redirect_to`: a string with the url to redirect to. Relative URLs also work. - `extra__redirect`: a callable that gets at least the keyword arguments `request`, `redirect_to`, `form`. Form that after create redirects to the edit page of the object: .. code-block:: python form = Form.create( auto__model=Album, extra__redirect=lambda form, **_: HttpResponseRedirect(form.instance.get_absolute_url() + 'edit/'), ) Form that after edit stays on the edit page: .. code-block:: python form = Form.edit( auto__instance=album, extra__redirect_to='.', ) How do I make a fields choices depend on another field? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The contents of the form is sent with any AJAX requests, so we can access the value of the other fields to do the filtering: .. code-block:: python def album_choices(form, **_): if form.fields.artist.value: return Album.objects.filter(artist=form.fields.artist.value) else: return Album.objects.all() .. code-block:: python Form( auto__model=Track, fields__artist=Field.choice_queryset( attr=None, choices=Artist.objects.all(), after=0, ), fields__album__choices=album_choices, ) How do I enable a reverse foreign key relationship? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default reverse foreign key relationships are hidden. To turn it on, pass `include=True` to the field. Note that these are read only, because the semantics of hijacking another models foreign keys would be quite weird. .. code-block:: python f = Form( auto__instance=artist, fields__albums__include=True, ) .. raw:: html
▼ Hide result
How do I set an initial value on a field that is not in the form? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You do have to include the field, but you can make it not rendered by using the `non_rendered` shortcut and setting `initial`. .. code-block:: python f = Form.create( auto__model=Album, fields__artist=Field.non_rendered(initial=black_sabbath), fields__year=Field.non_rendered(initial='1980'), ) .. raw:: html
▼ Hide result
If you post this form you will get this object: .. raw:: html
▼ Hide result
By default this will be non-editable, but you can allow editing (via the URL `GET` parameters) by setting `editable=True`. .. code-block:: python f = Form.create( auto__model=Album, fields__artist=Field.non_rendered(initial=black_sabbath), fields__year=Field.non_rendered( initial='1980', editable=True, ), ) .. raw:: html
► Show result
Accessing this create form with `?year=1999` in the title will create this object on submit: .. raw:: html
▼ Hide result
How do I group fields? ~~~~~~~~~~~~~~~~~~~~~~ Use the `group` field: .. code-block:: python form = Form( auto__model=Album, fields__year__group='metadata', fields__artist__group='metadata', ) .. raw:: html
▼ Hide result
How do I show a reverse many-to-many relationship? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default reverse many-to-many relationships are hidden. To turn it on, pass `include=True` to the field: .. code-block:: python form = Form( auto__model=Genre, instance=heavy_metal, fields__albums__include=True, ) .. raw:: html
▼ Hide result
How do I nest multiple forms? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python class MyNestedForm(Form): edit_dio = Form.edit(auto__instance=Artist.objects.get(name='Black Sabbath')) create_artist = Form.create(auto__model=Artist) create_album = Form.create(auto__model=Album) class Meta: actions__submit__post_handler = save_nested_forms .. raw:: html
▼ Hide result
.. code-block:: python heaven_and_hell = album .. _Form.fields_template: How do I use templates for fields? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes field groups just aren't enough and you may want to use a template to make your forms pretty: .. code-block:: python class CommentForm(Form): class Meta: # language=html fields_template = Template( {{ fields.album.input }}
{{ fields.name }}
{{ fields.email }}
{{ fields.comment }} ) name = Field() email = Field() comment = Field.textarea() album = Field.hardcoded(initial=heaven_and_hell) .. raw:: html
▼ Hide result