Forms#
How do I supply a custom parser for a field?#
Pass a callable to the parse
member of the field:
form = Form(
auto__model=Track,
fields__index__parse=
lambda field, string_value, **_: int(string_value[:-3]),
)
How do I make a field non-editable?#
Pass a callable or bool
to the editable
member of the field:
form = Form(
auto__model=Album,
fields__name__editable=
lambda request, **_: request.user.is_staff,
fields__artist__editable=False,
)
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:
form = Form.edit(
auto__instance=album,
editable=False,
)
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')
.
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!'),
)
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:
the
auto__include
parameter: this is a list of strings for members of the model to use to generate the form.the
auto__exclude
parameter: the inverse ofinclude
. If you use this the form gets all the fields from the model excluding the ones with names you supply inexclude
.for more advanced usages you can also pass the
include
parameter to a specific field likefields__my_field__include=True
. Here you can supply either abool
or a callable likefields__my_field__include=lambda request, **_: request.user.is_staff
.you can also add fields that are not present in the model by passing configuration like
fields__foo__attr='bar__baz'
(this means create aField
calledfoo
that reads its data frombar.baz
). You can either pass configuration data like that, or pass an entireField
instance.
How do I supply a custom initial value?#
Pass a value or callable to the initial
member:
form = Form(
auto__model=Album,
fields__name__initial='Paranoid',
fields__year__initial=lambda field, form, **_: 1970,
)
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.
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:
form = Form.create(
auto__model=Album,
fields__name__required=True,
fields__year__required=lambda field, form, **_: True,
)
To show the field as required before posting, you can add a CSS class rendering to your style definition:
IOMMI_DEFAULT_STYLE = Style(
bootstrap,
Field__attrs__class__required=lambda field, **_: field.required,
)
…and this CSS added to your sites custom style sheet:
.required label:after {
content: " *";
color: red;
}
For the following result:
See the style docs for more information on defining a custom style for your project.
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.
from iommi import LAST
form = Form(
auto__model=Album,
fields__name__after=LAST,
fields__year__after='artist',
fields__artist__after=0,
)
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.
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 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
:
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 Attrs.
How do I override rendering of an entire field?#
Pass a template name:
form = Form(
auto__model=Album,
fields__year__template='my_template.html',
)
or a Template
object:
form = Form(
auto__model=Album,
fields__year__template=Template('This is from the inline template'),
)
How do I override rendering of the input field?#
Pass a template name or a Template
object to the input
namespace:
form = Form(
auto__model=Album,
fields__year__input__template='my_template.html',
)
form = Form(
auto__model=Album,
fields__year__input__template=Template('This is from the inline template'),
)
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).
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).
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 argumentsrequest
,redirect_to
,form
.
Form that after create redirects to the edit page of the object:
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:
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:
def album_choices(form, **_):
if form.fields.artist.value:
return Album.objects.filter(artist=form.fields.artist.value)
else:
return Album.objects.all()
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 show 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.
f = Form(
auto__instance=artist,
fields__albums__include=True,
)
How 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
.
f = Form.create(
auto__model=Album,
fields__artist=Field.non_rendered(initial=black_sabbath),
fields__year=Field.non_rendered(initial='1980'),
)
If you post this form you will get this object:
By default this will be non-editable, but you can allow editing (via the
URL GET
parameters) by setting editable=True
.
f = Form.create(
auto__model=Album,
fields__artist=Field.non_rendered(initial=black_sabbath),
fields__year=Field.non_rendered(
initial='1980',
editable=True,
),
)
Accessing this create form with ?year=1999
in the title will create this object on submit:
How to I group fields?#
Use the group
field:
form = Form(
auto__model=Album,
fields__year__group='metadata',
fields__artist__group='metadata',
)
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:
form = Form(
auto__model=Genre,
instance=heavy_metal,
fields__albums__include=True,
)
How do I nest multiple forms?#
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