mirror of
https://github.com/django/django.git
synced 2024-11-29 14:46:18 +01:00
1022 lines
38 KiB
Plaintext
1022 lines
38 KiB
Plaintext
==========================
|
|
Creating forms from models
|
|
==========================
|
|
|
|
.. module:: django.forms.models
|
|
:synopsis: ModelForm and ModelFormset.
|
|
|
|
.. currentmodule:: django.forms
|
|
|
|
``ModelForm``
|
|
=============
|
|
.. class:: ModelForm
|
|
|
|
If you're building a database-driven app, chances are you'll have forms that
|
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
|
model, and you want to create a form that lets people submit comments. In this
|
|
case, it would be redundant to define the field types in your form, because
|
|
you've already defined the fields in your model.
|
|
|
|
For this reason, Django provides a helper class that let you create a ``Form``
|
|
class from a Django model.
|
|
|
|
For example::
|
|
|
|
>>> from django.forms import ModelForm
|
|
>>> from myapp.models import Article
|
|
|
|
# Create the form class.
|
|
>>> class ArticleForm(ModelForm):
|
|
... class Meta:
|
|
... model = Article
|
|
... fields = ['pub_date', 'headline', 'content', 'reporter']
|
|
|
|
# Creating a form to add an article.
|
|
>>> form = ArticleForm()
|
|
|
|
# Creating a form to change an existing article.
|
|
>>> article = Article.objects.get(pk=1)
|
|
>>> form = ArticleForm(instance=article)
|
|
|
|
Field types
|
|
-----------
|
|
|
|
The generated ``Form`` class will have a form field for every model field
|
|
specified, in the order specified in the ``fields`` attribute.
|
|
|
|
Each model field has a corresponding default form field. For example, a
|
|
``CharField`` on a model is represented as a ``CharField`` on a form. A model
|
|
``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is the
|
|
full list of conversions:
|
|
|
|
=============================== ========================================
|
|
Model field Form field
|
|
=============================== ========================================
|
|
``AutoField`` Not represented in the form
|
|
|
|
``BigIntegerField`` ``IntegerField`` with ``min_value`` set
|
|
to -9223372036854775808 and ``max_value``
|
|
set to 9223372036854775807.
|
|
|
|
``BooleanField`` ``BooleanField``
|
|
|
|
``CharField`` ``CharField`` with ``max_length`` set to
|
|
the model field's ``max_length``
|
|
|
|
``CommaSeparatedIntegerField`` ``CharField``
|
|
|
|
``DateField`` ``DateField``
|
|
|
|
``DateTimeField`` ``DateTimeField``
|
|
|
|
``DecimalField`` ``DecimalField``
|
|
|
|
``EmailField`` ``EmailField``
|
|
|
|
``FileField`` ``FileField``
|
|
|
|
``FilePathField`` ``CharField``
|
|
|
|
``FloatField`` ``FloatField``
|
|
|
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
|
|
|
``ImageField`` ``ImageField``
|
|
|
|
``IntegerField`` ``IntegerField``
|
|
|
|
``IPAddressField`` ``IPAddressField``
|
|
|
|
``GenericIPAddressField`` ``GenericIPAddressField``
|
|
|
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
|
below)
|
|
|
|
``NullBooleanField`` ``CharField``
|
|
|
|
``PositiveIntegerField`` ``IntegerField``
|
|
|
|
``PositiveSmallIntegerField`` ``IntegerField``
|
|
|
|
``SlugField`` ``SlugField``
|
|
|
|
``SmallIntegerField`` ``IntegerField``
|
|
|
|
``TextField`` ``CharField`` with
|
|
``widget=forms.Textarea``
|
|
|
|
``TimeField`` ``TimeField``
|
|
|
|
``URLField`` ``URLField``
|
|
=============================== ========================================
|
|
|
|
|
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
|
types are special cases:
|
|
|
|
* ``ForeignKey`` is represented by ``django.forms.ModelChoiceField``,
|
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
|
|
|
* ``ManyToManyField`` is represented by
|
|
``django.forms.ModelMultipleChoiceField``, which is a
|
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
|
|
|
In addition, each generated form field has attributes set as follows:
|
|
|
|
* If the model field has ``blank=True``, then ``required`` is set to
|
|
``False`` on the form field. Otherwise, ``required=True``.
|
|
|
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
|
field, with the first character capitalized.
|
|
|
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
|
field.
|
|
|
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
|
will be set to ``Select``, with choices coming from the model field's
|
|
``choices``. The choices will normally include the blank choice which is
|
|
selected by default. If the field is required, this forces the user to
|
|
make a selection. The blank choice will not be included if the model
|
|
field has ``blank=False`` and an explicit ``default`` value (the
|
|
``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.
|
|
|
|
A full example
|
|
--------------
|
|
|
|
Consider this set of models::
|
|
|
|
from django.db import models
|
|
from django.forms import ModelForm
|
|
|
|
TITLE_CHOICES = (
|
|
('MR', 'Mr.'),
|
|
('MRS', 'Mrs.'),
|
|
('MS', 'Ms.'),
|
|
)
|
|
|
|
class Author(models.Model):
|
|
name = models.CharField(max_length=100)
|
|
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
|
birth_date = models.DateField(blank=True, null=True)
|
|
|
|
def __unicode__(self):
|
|
return self.name
|
|
|
|
class Book(models.Model):
|
|
name = models.CharField(max_length=100)
|
|
authors = models.ManyToManyField(Author)
|
|
|
|
class AuthorForm(ModelForm):
|
|
class Meta:
|
|
model = Author
|
|
fields = ['name', 'title', 'birth_date']
|
|
|
|
class BookForm(ModelForm):
|
|
class Meta:
|
|
model = Book
|
|
fields = ['name', 'authors']
|
|
|
|
|
|
With these models, the ``ModelForm`` subclasses above would be roughly
|
|
equivalent to this (the only difference being the ``save()`` method, which
|
|
we'll discuss in a moment.)::
|
|
|
|
from django import forms
|
|
|
|
class AuthorForm(forms.Form):
|
|
name = forms.CharField(max_length=100)
|
|
title = forms.CharField(max_length=3,
|
|
widget=forms.Select(choices=TITLE_CHOICES))
|
|
birth_date = forms.DateField(required=False)
|
|
|
|
class BookForm(forms.Form):
|
|
name = forms.CharField(max_length=100)
|
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
|
|
|
.. _modelform-is-valid-and-errors:
|
|
|
|
The ``is_valid()`` method and ``errors``
|
|
----------------------------------------
|
|
|
|
The first time you call ``is_valid()`` or access the ``errors`` attribute of a
|
|
``ModelForm`` triggers :ref:`form validation <form-and-field-validation>` as
|
|
well as :ref:`model validation <validating-objects>`. This has the side-effect
|
|
of cleaning the model you pass to the ``ModelForm`` constructor. For instance,
|
|
calling ``is_valid()`` on your form will convert any date fields on your model
|
|
to actual date objects. If form validation fails, only some of the updates
|
|
may be applied. For this reason, you'll probably want to avoid reusing the
|
|
model instance passed to the form, especially if validation fails.
|
|
|
|
|
|
The ``save()`` method
|
|
---------------------
|
|
|
|
Every form produced by ``ModelForm`` also has a ``save()``
|
|
method. This method creates and saves a database object from the data
|
|
bound to the form. A subclass of ``ModelForm`` can accept an existing
|
|
model instance as the keyword argument ``instance``; if this is
|
|
supplied, ``save()`` will update that instance. If it's not supplied,
|
|
``save()`` will create a new instance of the specified model:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> from myapp.models import Article
|
|
>>> from myapp.forms import ArticleForm
|
|
|
|
# Create a form instance from POST data.
|
|
>>> f = ArticleForm(request.POST)
|
|
|
|
# Save a new Article object from the form's data.
|
|
>>> new_article = f.save()
|
|
|
|
# Create a form to edit an existing Article, but use
|
|
# POST data to populate the form.
|
|
>>> a = Article.objects.get(pk=1)
|
|
>>> f = ArticleForm(request.POST, instance=a)
|
|
>>> f.save()
|
|
|
|
Note that if the form :ref:`hasn't been validated
|
|
<modelform-is-valid-and-errors>`, calling ``save()`` will do so by checking
|
|
``form.errors``. A ``ValueError`` will be raised if the data in the form
|
|
doesn't validate -- i.e., if ``form.errors`` evaluates to ``True``.
|
|
|
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
|
the database. In this case, it's up to you to call ``save()`` on the resulting
|
|
model instance. This is useful if you want to do custom processing on the
|
|
object before saving it, or if you want to use one of the specialized
|
|
:ref:`model saving options <ref-models-force-insert>`. ``commit`` is ``True``
|
|
by default.
|
|
|
|
Another side effect of using ``commit=False`` is seen when your model has
|
|
a many-to-many relation with another model. If your model has a many-to-many
|
|
relation and you specify ``commit=False`` when you save a form, Django cannot
|
|
immediately save the form data for the many-to-many relation. This is because
|
|
it isn't possible to save many-to-many data for an instance until the instance
|
|
exists in the database.
|
|
|
|
To work around this problem, every time you save a form using ``commit=False``,
|
|
Django adds a ``save_m2m()`` method to your ``ModelForm`` subclass. After
|
|
you've manually saved the instance produced by the form, you can invoke
|
|
``save_m2m()`` to save the many-to-many form data. For example::
|
|
|
|
# Create a form instance with POST data.
|
|
>>> f = AuthorForm(request.POST)
|
|
|
|
# Create, but don't save the new author instance.
|
|
>>> new_author = f.save(commit=False)
|
|
|
|
# Modify the author in some way.
|
|
>>> new_author.some_field = 'some_value'
|
|
|
|
# Save the new instance.
|
|
>>> new_author.save()
|
|
|
|
# Now, save the many-to-many data for the form.
|
|
>>> f.save_m2m()
|
|
|
|
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
|
When you use a simple ``save()`` on a form, all data -- including
|
|
many-to-many data -- is saved without the need for any additional method calls.
|
|
For example::
|
|
|
|
# Create a form instance with POST data.
|
|
>>> a = Author()
|
|
>>> f = AuthorForm(request.POST, instance=a)
|
|
|
|
# Create and save the new author instance. There's no need to do anything else.
|
|
>>> new_author = f.save()
|
|
|
|
Other than the ``save()`` and ``save_m2m()`` methods, a ``ModelForm`` works
|
|
exactly the same way as any other ``forms`` form. For example, the
|
|
``is_valid()`` method is used to check for validity, the ``is_multipart()``
|
|
method is used to determine whether a form requires multipart file upload (and
|
|
hence whether ``request.FILES`` must be passed to the form), etc. See
|
|
:ref:`binding-uploaded-files` for more information.
|
|
|
|
.. _modelforms-selecting-fields:
|
|
|
|
Selecting the fields to use
|
|
---------------------------
|
|
|
|
It is strongly recommended that you explicitly set all fields that should be
|
|
edited in the form using the ``fields`` attribute. Failure to do so can easily
|
|
lead to security problems when a form unexpectedly allows a user to set certain
|
|
fields, especially when new fields are added to a model. Depending on how the
|
|
form is rendered, the problem may not even be visible on the web page.
|
|
|
|
The alternative approach would be to include all fields automatically, or
|
|
blacklist only some. This fundamental approach is known to be much less secure
|
|
and has led to serious exploits on major websites (e.g. `GitHub
|
|
<https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation>`_).
|
|
|
|
There are, however, two shortcuts available for cases where you can guarantee
|
|
these security concerns do not apply to you:
|
|
|
|
1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate
|
|
that all fields in the model should be used. For example::
|
|
|
|
from django.forms import ModelForm
|
|
|
|
class AuthorForm(ModelForm):
|
|
class Meta:
|
|
model = Author
|
|
fields = '__all__'
|
|
|
|
2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to
|
|
a list of fields to be excluded from the form.
|
|
|
|
For example::
|
|
|
|
class PartialAuthorForm(ModelForm):
|
|
class Meta:
|
|
model = Author
|
|
exclude = ['title']
|
|
|
|
Since the ``Author`` model has the 3 fields ``name``, ``title`` and
|
|
``birth_date``, this will result in the fields ``name`` and ``birth_date``
|
|
being present on the form.
|
|
|
|
If either of these are used, the order the fields appear in the form will be the
|
|
order the fields are defined in the model, with ``ManyToManyField`` instances
|
|
appearing last.
|
|
|
|
In addition, Django applies the following rule: if you set ``editable=False`` on
|
|
the model field, *any* form created from the model via ``ModelForm`` will not
|
|
include that field.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
Before version 1.6, the ``'__all__'`` shortcut did not exist, but omitting
|
|
the ``fields`` attribute had the same effect. Omitting both ``fields`` and
|
|
``exclude`` is now deprecated, but will continue to work as before until
|
|
version 1.8
|
|
|
|
|
|
.. note::
|
|
|
|
Any fields not included in a form by the above logic
|
|
will not be set by the form's ``save()`` method. Also, if you
|
|
manually add the excluded fields back to the form, they will not
|
|
be initialized from the model instance.
|
|
|
|
Django will prevent any attempt to save an incomplete model, so if
|
|
the model does not allow the missing fields to be empty, and does
|
|
not provide a default value for the missing fields, any attempt to
|
|
``save()`` a ``ModelForm`` with missing fields will fail. To
|
|
avoid this failure, you must instantiate your model with initial
|
|
values for the missing, but required fields::
|
|
|
|
author = Author(title='Mr')
|
|
form = PartialAuthorForm(request.POST, instance=author)
|
|
form.save()
|
|
|
|
Alternatively, you can use ``save(commit=False)`` and manually set
|
|
any extra required fields::
|
|
|
|
form = PartialAuthorForm(request.POST)
|
|
author = form.save(commit=False)
|
|
author.title = 'Mr'
|
|
author.save()
|
|
|
|
See the `section on saving forms`_ for more details on using
|
|
``save(commit=False)``.
|
|
|
|
.. _section on saving forms: `The save() method`_
|
|
|
|
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 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`` for the ``name``
|
|
attribute of ``Author`` to be represented by a ``<textarea>`` instead
|
|
of its default ``<input type="text">``, you can override the field's
|
|
widget::
|
|
|
|
from django.forms import ModelForm, Textarea
|
|
from myapp.models import Author
|
|
|
|
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::
|
|
|
|
from django.forms import ModelForm
|
|
from myapp.models import Article
|
|
|
|
class ArticleForm(ModelForm):
|
|
pub_date = MyDateFormField()
|
|
|
|
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::
|
|
|
|
from django.forms import ModelForm, DateField
|
|
from myapp.models import Article
|
|
|
|
class ArticleForm(ModelForm):
|
|
pub_date = DateField(label='Publication date')
|
|
|
|
class Meta:
|
|
model = Article
|
|
fields = ['pub_date', 'headline', 'content', 'reporter']
|
|
|
|
|
|
.. note::
|
|
|
|
If you explicitly instantiate a form field like this, Django assumes that you
|
|
want to completely define its behavior; therefore, default attributes (such as
|
|
``max_length`` or ``required``) are not drawn from the corresponding model. If
|
|
you want to maintain the behavior specified in the model, you must set the
|
|
relevant arguments explicitly when declaring the form field.
|
|
|
|
For example, if the ``Article`` model looks like this::
|
|
|
|
class Article(models.Model):
|
|
headline = models.CharField(max_length=200, null=True, blank=True,
|
|
help_text="Use puns liberally")
|
|
content = models.TextField()
|
|
|
|
and you want to do some custom validation for ``headline``, while keeping
|
|
the ``blank`` and ``help_text`` values as specified, you might define
|
|
``ArticleForm`` like this::
|
|
|
|
class ArticleForm(ModelForm):
|
|
headline = MyFormField(max_length=200, required=False,
|
|
help_text="Use puns liberally")
|
|
|
|
class Meta:
|
|
model = Article
|
|
fields = ['headline', 'content']
|
|
|
|
You must ensure that the type of the form field can be used to set the
|
|
contents of the corresponding model field. When they are not compatible,
|
|
you will get a ``ValueError`` as no implicit conversion takes place.
|
|
|
|
See the :doc:`form field documentation </ref/forms/fields>` for more information
|
|
on fields and their arguments.
|
|
|
|
|
|
Enabling localization of fields
|
|
-------------------------------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
By default, the fields in a ``ModelForm`` will not localize their data. To
|
|
enable localization for fields, you can use the ``localized_fields``
|
|
attribute on the ``Meta`` class.
|
|
|
|
>>> from django.forms import ModelForm
|
|
>>> from myapp.models import Author
|
|
>>> class AuthorForm(ModelForm):
|
|
... class Meta:
|
|
... model = Author
|
|
... localized_fields = ('birth_date',)
|
|
|
|
If ``localized_fields`` is set to the special value ``'__all__'``, all fields
|
|
will be localized.
|
|
|
|
.. _overriding-modelform-clean-method:
|
|
|
|
Overriding the clean() method
|
|
-----------------------------
|
|
|
|
You can override the ``clean()`` method on a model form to provide additional
|
|
validation in the same way you can on a normal form.
|
|
|
|
In this regard, model forms have two specific characteristics when compared to
|
|
forms:
|
|
|
|
By default the ``clean()`` method validates the uniqueness of fields that are
|
|
marked as ``unique``, ``unique_together`` or ``unique_for_date|month|year`` on
|
|
the model. Therefore, if you would like to override the ``clean()`` method and
|
|
maintain the default validation, you must call the parent class's ``clean()``
|
|
method.
|
|
|
|
Also, a model form instance bound to a model object will contain a
|
|
``self.instance`` attribute that gives model form methods access to that
|
|
specific model instance.
|
|
|
|
Form inheritance
|
|
----------------
|
|
|
|
As with basic forms, you can extend and reuse ``ModelForms`` by inheriting
|
|
them. This is useful if you need to declare extra fields or extra methods on a
|
|
parent class for use in a number of forms derived from models. For example,
|
|
using the previous ``ArticleForm`` class::
|
|
|
|
>>> class EnhancedArticleForm(ArticleForm):
|
|
... def clean_pub_date(self):
|
|
... ...
|
|
|
|
This creates a form that behaves identically to ``ArticleForm``, except there's
|
|
some extra validation and cleaning for the ``pub_date`` field.
|
|
|
|
You can also subclass the parent's ``Meta`` inner class if you want to change
|
|
the ``Meta.fields`` or ``Meta.excludes`` lists::
|
|
|
|
>>> class RestrictedArticleForm(EnhancedArticleForm):
|
|
... class Meta(ArticleForm.Meta):
|
|
... exclude = ('body',)
|
|
|
|
This adds the extra method from the ``EnhancedArticleForm`` and modifies
|
|
the original ``ArticleForm.Meta`` to remove one field.
|
|
|
|
There are a couple of things to note, however.
|
|
|
|
* Normal Python name resolution rules apply. If you have multiple base
|
|
classes that declare a ``Meta`` inner class, only the first one will be
|
|
used. This means the child's ``Meta``, if it exists, otherwise the
|
|
``Meta`` of the first parent, etc.
|
|
|
|
* For technical reasons, a subclass cannot inherit from both a ``ModelForm``
|
|
and a ``Form`` simultaneously.
|
|
|
|
Chances are these notes won't affect you unless you're trying to do something
|
|
tricky with subclassing.
|
|
|
|
Interaction with model validation
|
|
---------------------------------
|
|
|
|
As part of its validation process, ``ModelForm`` will call the ``clean()``
|
|
method of each field on your model that has a corresponding field on your form.
|
|
If you have excluded any model fields, validation will not be run on those
|
|
fields. See the :doc:`form validation </ref/forms/validation>` documentation
|
|
for more on how field cleaning and validation work. Also, your model's
|
|
``clean()`` method will be called before any uniqueness checks are made. See
|
|
:ref:`Validating objects <validating-objects>` for more information on the
|
|
model's ``clean()`` hook.
|
|
|
|
.. _modelforms-factory:
|
|
|
|
ModelForm factory function
|
|
--------------------------
|
|
|
|
You can create forms from a given model using the standalone function
|
|
:func:`~django.forms.models.modelform_factory`, instead of using a class
|
|
definition. This may be more convenient if you do not have many customizations
|
|
to make::
|
|
|
|
>>> from django.forms.models import modelform_factory
|
|
>>> from myapp.models import Book
|
|
>>> BookForm = modelform_factory(Book, fields=("author", "title"))
|
|
|
|
This can also be used to make simple modifications to existing forms, for
|
|
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()})
|
|
|
|
The fields to include can be specified using the ``fields`` and ``exclude``
|
|
keyword arguments, or the corresponding attributes on the ``ModelForm`` inner
|
|
``Meta`` class. Please see the ``ModelForm`` :ref:`modelforms-selecting-fields`
|
|
documentation.
|
|
|
|
... or enable localization for specific fields::
|
|
|
|
>>> Form = modelform_factory(Author, form=AuthorForm, localized_fields=("birth_date",))
|
|
|
|
.. _model-formsets:
|
|
|
|
Model formsets
|
|
==============
|
|
|
|
.. class:: models.BaseModelFormSet
|
|
|
|
Like :doc:`regular formsets </topics/forms/formsets>`, Django provides a couple
|
|
of enhanced formset classes that make it easy to work with Django models. Let's
|
|
reuse the ``Author`` model from above::
|
|
|
|
>>> from django.forms.models import modelformset_factory
|
|
>>> from myapp.models import Author
|
|
>>> AuthorFormSet = modelformset_factory(Author)
|
|
|
|
This will create a formset that is capable of working with the data associated
|
|
with the ``Author`` model. It works just like a regular formset::
|
|
|
|
>>> formset = AuthorFormSet()
|
|
>>> print(formset)
|
|
<input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" /><input type="hidden" name="form-MAX_NUM_FORMS" id="id_form-MAX_NUM_FORMS" />
|
|
<tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></td></tr>
|
|
<tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title">
|
|
<option value="" selected="selected">---------</option>
|
|
<option value="MR">Mr.</option>
|
|
<option value="MRS">Mrs.</option>
|
|
<option value="MS">Ms.</option>
|
|
</select></td></tr>
|
|
<tr><th><label for="id_form-0-birth_date">Birth date:</label></th><td><input type="text" name="form-0-birth_date" id="id_form-0-birth_date" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></td></tr>
|
|
|
|
.. note::
|
|
|
|
:func:`~django.forms.models.modelformset_factory` uses
|
|
:func:`~django.forms.formsets.formset_factory` to generate formsets. This
|
|
means that a model formset is just an extension of a basic formset that
|
|
knows how to interact with a particular model.
|
|
|
|
Changing the queryset
|
|
---------------------
|
|
|
|
By default, when you create a formset from a model, the formset will use a
|
|
queryset that includes all objects in the model (e.g.,
|
|
``Author.objects.all()``). You can override this behavior by using the
|
|
``queryset`` argument::
|
|
|
|
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
|
|
|
|
Alternatively, you can create a subclass that sets ``self.queryset`` in
|
|
``__init__``::
|
|
|
|
from django.forms.models import BaseModelFormSet
|
|
from myapp.models import Author
|
|
|
|
class BaseAuthorFormSet(BaseModelFormSet):
|
|
def __init__(self, *args, **kwargs):
|
|
super(BaseAuthorFormSet, self).__init__(*args, **kwargs)
|
|
self.queryset = Author.objects.filter(name__startswith='O')
|
|
|
|
Then, pass your ``BaseAuthorFormSet`` class to the factory function::
|
|
|
|
>>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet)
|
|
|
|
If you want to return a formset that doesn't include *any* pre-existing
|
|
instances of the model, you can specify an empty QuerySet::
|
|
|
|
>>> AuthorFormSet(queryset=Author.objects.none())
|
|
|
|
|
|
Controlling which fields are used with ``fields`` and ``exclude``
|
|
-----------------------------------------------------------------
|
|
|
|
By default, a model formset uses all fields in the model that are not marked
|
|
with ``editable=False``. However, this can be overridden at the formset level::
|
|
|
|
>>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
|
|
|
|
Using ``fields`` restricts the formset to use only the given fields.
|
|
Alternatively, you can take an "opt-out" approach, specifying which fields to
|
|
exclude::
|
|
|
|
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
|
|
|
|
Specifying widgets to use in the form with ``widgets``
|
|
------------------------------------------------------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
Using the ``widgets`` parameter, you can specify a dictionary of values to
|
|
customize the ``ModelForm``'s widget class for a particular field. This
|
|
works the same way as the ``widgets`` dictionary on the inner ``Meta``
|
|
class of a ``ModelForm`` works::
|
|
|
|
>>> AuthorFormSet = modelformset_factory(
|
|
... Author, widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})
|
|
|
|
Enabling localization for fields with ``localized_fields``
|
|
----------------------------------------------------------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
Using the ``localized_fields`` parameter, you can enable localization for
|
|
fields in the form.
|
|
|
|
>>> AuthorFormSet = modelformset_factory(
|
|
... Author, localized_fields=('value',))
|
|
|
|
If ``localized_fields`` is set to the special value ``'__all__'``, all fields
|
|
will be localized.
|
|
|
|
Providing initial values
|
|
------------------------
|
|
|
|
As with regular formsets, it's possible to :ref:`specify initial data
|
|
<formsets-initial-data>` for forms in the formset by specifying an ``initial``
|
|
parameter when instantiating the model formset class returned by
|
|
:func:`~django.forms.models.modelformset_factory`. However, with model
|
|
formsets, the initial values only apply to extra forms, those that aren't bound
|
|
to an existing object instance.
|
|
|
|
.. _saving-objects-in-the-formset:
|
|
|
|
Saving objects in the formset
|
|
-----------------------------
|
|
|
|
As with a ``ModelForm``, you can save the data as a model object. This is done
|
|
with the formset's ``save()`` method::
|
|
|
|
# Create a formset instance with POST data.
|
|
>>> formset = AuthorFormSet(request.POST)
|
|
|
|
# Assuming all is valid, save the data.
|
|
>>> instances = formset.save()
|
|
|
|
The ``save()`` method returns the instances that have been saved to the
|
|
database. If a given instance's data didn't change in the bound data, the
|
|
instance won't be saved to the database and won't be included in the return
|
|
value (``instances``, in the above example).
|
|
|
|
When fields are missing from the form (for example because they have been
|
|
excluded), these fields will not be set by the ``save()`` method. You can find
|
|
more information about this restriction, which also holds for regular
|
|
``ModelForms``, in `Selecting the fields to use`_.
|
|
|
|
Pass ``commit=False`` to return the unsaved model instances::
|
|
|
|
# don't save to the database
|
|
>>> instances = formset.save(commit=False)
|
|
>>> for instance in instances:
|
|
... # do something with instance
|
|
... instance.save()
|
|
|
|
This gives you the ability to attach data to the instances before saving them
|
|
to the database. If your formset contains a ``ManyToManyField``, you'll also
|
|
need to call ``formset.save_m2m()`` to ensure the many-to-many relationships
|
|
are saved properly.
|
|
|
|
.. _model-formsets-max-num:
|
|
|
|
Limiting the number of editable objects
|
|
---------------------------------------
|
|
|
|
As with regular formsets, you can use the ``max_num`` and ``extra`` parameters
|
|
to :func:`~django.forms.models.modelformset_factory` to limit the number of
|
|
extra forms displayed.
|
|
|
|
``max_num`` does not prevent existing objects from being displayed::
|
|
|
|
>>> Author.objects.order_by('name')
|
|
[<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>]
|
|
|
|
>>> AuthorFormSet = modelformset_factory(Author, max_num=1)
|
|
>>> formset = AuthorFormSet(queryset=Author.objects.order_by('name'))
|
|
>>> [x.name for x in formset.get_queryset()]
|
|
[u'Charles Baudelaire', u'Paul Verlaine', u'Walt Whitman']
|
|
|
|
If the value of ``max_num`` is greater than the number of existing related
|
|
objects, up to ``extra`` additional blank forms will be added to the formset,
|
|
so long as the total number of forms does not exceed ``max_num``::
|
|
|
|
>>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=2)
|
|
>>> formset = AuthorFormSet(queryset=Author.objects.order_by('name'))
|
|
>>> for form in formset:
|
|
... print(form.as_table())
|
|
<tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr>
|
|
<tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100" /><input type="hidden" name="form-1-id" value="3" id="id_form-1-id" /></td></tr>
|
|
<tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr>
|
|
<tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr>
|
|
|
|
A ``max_num`` value of ``None`` (the default) puts a high limit on the number
|
|
of forms displayed (1000). In practice this is equivalent to no limit.
|
|
|
|
Using a model formset in a view
|
|
-------------------------------
|
|
|
|
Model formsets are very similar to formsets. Let's say we want to present a
|
|
formset to edit ``Author`` model instances::
|
|
|
|
from django.forms.models import modelformset_factory
|
|
from django.shortcuts import render_to_response
|
|
from myapp.models import Author
|
|
|
|
def manage_authors(request):
|
|
AuthorFormSet = modelformset_factory(Author)
|
|
if request.method == 'POST':
|
|
formset = AuthorFormSet(request.POST, request.FILES)
|
|
if formset.is_valid():
|
|
formset.save()
|
|
# do something.
|
|
else:
|
|
formset = AuthorFormSet()
|
|
return render_to_response("manage_authors.html", {
|
|
"formset": formset,
|
|
})
|
|
|
|
As you can see, the view logic of a model formset isn't drastically different
|
|
than that of a "normal" formset. The only difference is that we call
|
|
``formset.save()`` to save the data into the database. (This was described
|
|
above, in :ref:`saving-objects-in-the-formset`.)
|
|
|
|
Overiding ``clean()`` on a ``model_formset``
|
|
--------------------------------------------
|
|
|
|
Just like with ``ModelForms``, by default the ``clean()`` method of a
|
|
``model_formset`` will validate that none of the items in the formset violate
|
|
the unique constraints on your model (either ``unique``, ``unique_together`` or
|
|
``unique_for_date|month|year``). If you want to override the ``clean()`` method
|
|
on a ``model_formset`` and maintain this validation, you must call the parent
|
|
class's ``clean`` method::
|
|
|
|
from django.forms.models import BaseModelFormSet
|
|
|
|
class MyModelFormSet(BaseModelFormSet):
|
|
def clean(self):
|
|
super(MyModelFormSet, self).clean()
|
|
# example custom validation across forms in the formset:
|
|
for form in self.forms:
|
|
# your custom formset validation
|
|
pass
|
|
|
|
Using a custom queryset
|
|
-----------------------
|
|
|
|
As stated earlier, you can override the default queryset used by the model
|
|
formset::
|
|
|
|
from django.forms.models import modelformset_factory
|
|
from django.shortcuts import render_to_response
|
|
from myapp.models import Author
|
|
|
|
def manage_authors(request):
|
|
AuthorFormSet = modelformset_factory(Author)
|
|
if request.method == "POST":
|
|
formset = AuthorFormSet(request.POST, request.FILES,
|
|
queryset=Author.objects.filter(name__startswith='O'))
|
|
if formset.is_valid():
|
|
formset.save()
|
|
# Do something.
|
|
else:
|
|
formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
|
|
return render_to_response("manage_authors.html", {
|
|
"formset": formset,
|
|
})
|
|
|
|
Note that we pass the ``queryset`` argument in both the ``POST`` and ``GET``
|
|
cases in this example.
|
|
|
|
Using the formset in the template
|
|
---------------------------------
|
|
|
|
.. highlight:: html+django
|
|
|
|
There are three ways to render a formset in a Django template.
|
|
|
|
First, you can let the formset do most of the work::
|
|
|
|
<form method="post" action="">
|
|
{{ formset }}
|
|
</form>
|
|
|
|
Second, you can manually render the formset, but let the form deal with
|
|
itself::
|
|
|
|
<form method="post" action="">
|
|
{{ formset.management_form }}
|
|
{% for form in formset %}
|
|
{{ form }}
|
|
{% endfor %}
|
|
</form>
|
|
|
|
When you manually render the forms yourself, be sure to render the management
|
|
form as shown above. See the :ref:`management form documentation
|
|
<understanding-the-managementform>`.
|
|
|
|
Third, you can manually render each field::
|
|
|
|
<form method="post" action="">
|
|
{{ formset.management_form }}
|
|
{% for form in formset %}
|
|
{% for field in form %}
|
|
{{ field.label_tag }}: {{ field }}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
</form>
|
|
|
|
If you opt to use this third method and you don't iterate over the fields with
|
|
a ``{% for %}`` loop, you'll need to render the primary key field. For example,
|
|
if you were rendering the ``name`` and ``age`` fields of a model::
|
|
|
|
<form method="post" action="">
|
|
{{ formset.management_form }}
|
|
{% for form in formset %}
|
|
{{ form.id }}
|
|
<ul>
|
|
<li>{{ form.name }}</li>
|
|
<li>{{ form.age }}</li>
|
|
</ul>
|
|
{% endfor %}
|
|
</form>
|
|
|
|
Notice how we need to explicitly render ``{{ form.id }}``. This ensures that
|
|
the model formset, in the ``POST`` case, will work correctly. (This example
|
|
assumes a primary key named ``id``. If you've explicitly defined your own
|
|
primary key that isn't called ``id``, make sure it gets rendered.)
|
|
|
|
.. highlight:: python
|
|
|
|
.. _inline-formsets:
|
|
|
|
Inline formsets
|
|
===============
|
|
|
|
Inline formsets is a small abstraction layer on top of model formsets. These
|
|
simplify the case of working with related objects via a foreign key. Suppose
|
|
you have these two models::
|
|
|
|
from django.db import models
|
|
|
|
class Author(models.Model):
|
|
name = models.CharField(max_length=100)
|
|
|
|
class Book(models.Model):
|
|
author = models.ForeignKey(Author)
|
|
title = models.CharField(max_length=100)
|
|
|
|
If you want to create a formset that allows you to edit books belonging to
|
|
a particular author, you could do this::
|
|
|
|
>>> from django.forms.models import inlineformset_factory
|
|
>>> BookFormSet = inlineformset_factory(Author, Book)
|
|
>>> author = Author.objects.get(name=u'Mike Royko')
|
|
>>> formset = BookFormSet(instance=author)
|
|
|
|
.. note::
|
|
|
|
:func:`~django.forms.models.inlineformset_factory` uses
|
|
:func:`~django.forms.models.modelformset_factory` and marks
|
|
``can_delete=True``.
|
|
|
|
.. seealso::
|
|
|
|
:ref:`Manually rendered can_delete and can_order <manually-rendered-can-delete-and-can-order>`.
|
|
|
|
More than one foreign key to the same model
|
|
-------------------------------------------
|
|
|
|
If your model contains more than one foreign key to the same model, you'll
|
|
need to resolve the ambiguity manually using ``fk_name``. For example, consider
|
|
the following model::
|
|
|
|
class Friendship(models.Model):
|
|
from_friend = models.ForeignKey(Friend)
|
|
to_friend = models.ForeignKey(Friend)
|
|
length_in_months = models.IntegerField()
|
|
|
|
To resolve this, you can use ``fk_name`` to
|
|
:func:`~django.forms.models.inlineformset_factory`::
|
|
|
|
>>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name="from_friend")
|
|
|
|
Using an inline formset in a view
|
|
---------------------------------
|
|
|
|
You may want to provide a view that allows a user to edit the related objects
|
|
of a model. Here's how you can do that::
|
|
|
|
def manage_books(request, author_id):
|
|
author = Author.objects.get(pk=author_id)
|
|
BookInlineFormSet = inlineformset_factory(Author, Book)
|
|
if request.method == "POST":
|
|
formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
|
|
if formset.is_valid():
|
|
formset.save()
|
|
# Do something. Should generally end with a redirect. For example:
|
|
return HttpResponseRedirect(author.get_absolute_url())
|
|
else:
|
|
formset = BookInlineFormSet(instance=author)
|
|
return render_to_response("manage_books.html", {
|
|
"formset": formset,
|
|
})
|
|
|
|
Notice how we pass ``instance`` in both the ``POST`` and ``GET`` cases.
|
|
|
|
Specifying widgets to use in the inline form
|
|
--------------------------------------------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
``inlineformset_factory`` uses ``modelformset_factory`` and passes most
|
|
of its arguments to ``modelformset_factory``. This means you can use
|
|
the ``widgets`` parameter in much the same way as passing it to
|
|
``modelformset_factory``. See `Specifying widgets to use in the form with widgets`_ above.
|