Did some much-needed rewriting/editing in the formsets section of docs/topics/forms/modelsforms.txt. 'It self' is not two words.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9616 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2008-12-09 04:17:14 +00:00
parent 6553ddc5b4
commit c006ef5746
1 changed files with 73 additions and 77 deletions

View File

@ -385,19 +385,18 @@ tricky with subclassing.
.. _model-formsets: .. _model-formsets:
Model Formsets Model formsets
============== ==============
Similar to :ref:`regular formsets <topics-forms-formsets>` there are a couple Like :ref:`regular formsets <topics-forms-formsets>`, Django provides a couple
enhanced formset classes that provide all the right things to work with your of enhanced formset classes that make it easy to work with Django models. Let's
models. Lets reuse the ``Author`` model from above:: reuse the ``Author`` model from above::
>>> from django.forms.models import modelformset_factory >>> from django.forms.models import modelformset_factory
>>> AuthorFormSet = modelformset_factory(Author) >>> AuthorFormSet = modelformset_factory(Author)
This will create a formset that is capable of working with the data associated This will create a formset that is capable of working with the data associated
to the ``Author`` model. It works just like a regular formset just that we are with the ``Author`` model. It works just like a regular formset::
working with ``ModelForm`` instances instead of ``Form`` instances::
>>> formset = AuthorFormSet() >>> formset = AuthorFormSet()
>>> print formset >>> print formset
@ -419,13 +418,15 @@ working with ``ModelForm`` instances instead of ``Form`` instances::
Changing the queryset Changing the queryset
--------------------- ---------------------
By default when you create a formset from a model the queryset will be all By default, when you create a formset from a model, the formset will use a
objects in the model. This is best shown as ``Author.objects.all()``. This is queryset that includes all objects in the model (e.g.,
configurable:: ``Author.objects.all()``). You can override this behavior by using the
``queryset`` argument::
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) >>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
Alternatively, you can use a subclassing based approach:: Alternatively, you can create a subclass that implements a ``get_queryset()``
method::
from django.forms.models import BaseModelFormSet from django.forms.models import BaseModelFormSet
@ -433,46 +434,44 @@ Alternatively, you can use a subclassing based approach::
def get_queryset(self): def get_queryset(self):
return super(BaseAuthorFormSet, self).get_queryset().filter(name__startswith='O') return super(BaseAuthorFormSet, self).get_queryset().filter(name__startswith='O')
Then your ``BaseAuthorFormSet`` would be passed into the factory function to Then, pass your ``BaseAuthorFormSet`` class to the factory function::
be used as a base::
>>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) >>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet)
Controlling which fields are used with ``fields`` and ``exclude`` Controlling which fields are used with ``fields`` and ``exclude``
----------------------------------------------------------------- -----------------------------------------------------------------
By default a model formset will use all fields in the model that are not marked By default, a model formset uses all fields in the model that are not marked
with ``editable=False``. However, this can be overidden at the formset level:: with ``editable=False``. However, this can be overridden at the formset level::
>>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) >>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
Using ``fields`` will restrict the formset to use just the given fields. Or if Using ``fields`` restricts the formset to use only the given fields.
you need to go the other way:: Alternatively, you can take an "opt-out" approach, specifying which fields to
exclude::
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',)) >>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
Using ``exclude`` will prevent the given fields from being used in the formset.
.. _saving-objects-in-the-formset: .. _saving-objects-in-the-formset:
Saving objects in the formset Saving objects in the formset
----------------------------- -----------------------------
Similar to a ``ModelForm`` you can save the data into the model. This is done As with a ``ModelForm``, you can save the data as a model object. This is done
with the ``save()`` method on the formset:: with the formset's ``save()`` method::
# create a formset instance with POST data. # Create a formset instance with POST data.
>>> formset = AuthorFormSet(request.POST) >>> formset = AuthorFormSet(request.POST)
# assuming all is valid, save the data # Assuming all is valid, save the data.
>>> instances = formset.save() >>> instances = formset.save()
The ``save()`` method will return the instances that have been saved to the The ``save()`` method returns the instances that have been saved to the
database. If an instance did not change in the bound data it will not be database. If a given instance's data didn't change in the bound data, the
saved to the database and not found in ``instances`` in the above example. instance won't be saved to the database and won't be included in the return
value (``instances``, in the above example).
You can optionally pass in ``commit=False`` to ``save()`` to only return the Pass ``commit=False`` to return the unsaved model instances::
model instances without any database interaction::
# don't save to the database # don't save to the database
>>> instances = formset.save(commit=False) >>> instances = formset.save(commit=False)
@ -481,18 +480,18 @@ model instances without any database interaction::
... instance.save() ... instance.save()
This gives you the ability to attach data to the instances before saving them This gives you the ability to attach data to the instances before saving them
to the database. If your formset contains a ``ManyToManyField`` you will also to the database. If your formset contains a ``ManyToManyField``, you'll also
need to make a call to ``formset.save_m2m()`` to ensure the many-to-many need to call ``formset.save_m2m()`` to ensure the many-to-many relationships
relationships are saved properly. are saved properly.
.. _model-formsets-max-num: .. _model-formsets-max-num:
Limiting the number of objects editable Limiting the number of editable objects
--------------------------------------- ---------------------------------------
Similar to regular formsets you can use the ``max_num`` parameter to As with regular formsets, you can use the ``max_num`` parameter to
``modelformset_factory`` to limit the number of forms displayed. With ``modelformset_factory`` to limit the number of forms displayed. With
model formsets this will properly limit the query to only select the maximum model formsets, this properly limits the query to select only the maximum
number of objects needed:: number of objects needed::
>>> Author.objects.order_by('name') >>> Author.objects.order_by('name')
@ -503,8 +502,8 @@ number of objects needed::
>>> formset.initial >>> formset.initial
[{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}] [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}]
If the value of ``max_num`` is less than the total objects returned it will If the value of ``max_num`` is less than the total objects returned, the
fill the rest with extra forms:: formset will fill the rest with extra forms::
>>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=1) >>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=1)
>>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name'))
@ -518,8 +517,8 @@ fill the rest with extra forms::
Using a model formset in a view Using a model formset in a view
------------------------------- -------------------------------
Model formsets are very similar to formsets. Lets say we want to present a Model formsets are very similar to formsets. Let's say we want to present a
formset to a user to edit ``Author`` model instances:: formset to edit ``Author`` model instances::
def manage_authors(request): def manage_authors(request):
AuthorFormSet = modelformset_factory(Author) AuthorFormSet = modelformset_factory(Author)
@ -534,16 +533,16 @@ formset to a user to edit ``Author`` model instances::
"formset": formset, "formset": formset,
}) })
As you can see the view is not drastically different than how to use a formset As you can see, the view logic of a model formset isn't drastically different
in a view. The only difference is that we call ``formset.save()`` to save the than that of a "normal" formset. The only difference is that we call
data into the database. This is described above in ``formset.save()`` to save the data into the database. (This was described
:ref:`saving-objects-in-the-formset`. above, in :ref:`saving-objects-in-the-formset`.)
Using a custom queryset Using a custom queryset
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
As stated earlier you can override the default queryset the model formset As stated earlier, you can override the default queryset used by the model
uses:: formset::
def manage_authors(request): def manage_authors(request):
AuthorFormSet = modelformset_factory(Author) AuthorFormSet = modelformset_factory(Author)
@ -552,27 +551,29 @@ uses::
queryset=Author.objects.filter(name__startswith='O')) queryset=Author.objects.filter(name__startswith='O'))
if formset.is_valid(): if formset.is_valid():
formset.save() formset.save()
# do something. # Do something.
else: else:
formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
return render_to_response("manage_authors.html", { return render_to_response("manage_authors.html", {
"formset": formset, "formset": formset,
}) })
What is critical to point out here is that you must pass the queryset in both Note that we pass the ``queryset`` argument in both the ``POST`` and ``GET``
the ``POST`` and ``GET`` cases shown above. cases in this example.
Using the formset in the template Using the formset in the template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are three ways you might want to render the formset in your template. There are three ways to render a formset in a Django template.
You can let the formset do most of the work::
First, you can let the formset do most of the work::
<form method="POST" action=""> <form method="POST" action="">
{{ formset }} {{ formset }}
</form> </form>
You can manually render the formset, but let the form deal with it self:: Second, you can manually render the formset, but let the form deal with
itself::
<form method="POST" action=""> <form method="POST" action="">
{{ formset.management_form }} {{ formset.management_form }}
@ -582,9 +583,9 @@ You can manually render the formset, but let the form deal with it self::
</form> </form>
When you manually render the forms yourself, be sure to render the management When you manually render the forms yourself, be sure to render the management
form as shown above. Also see the :ref:`management form documentation <understanding-the-managementform>`. form as shown above. See the :ref:`management form documentation <understanding-the-managementform>`.
Or you can just do it all yourself:: Third, you can manually render each field::
<form method="POST" action=""> <form method="POST" action="">
{{ formset.management_form }} {{ formset.management_form }}
@ -595,10 +596,9 @@ Or you can just do it all yourself::
{% endfor %} {% endfor %}
</form> </form>
It is critical to note that if you opt to do most of the work yourself and you If you opt to use this third method and you don't iterate over the fields with
don't go with a field ``{% for %}`` loop of the form, as shown in the last a ``{% for %}`` loop, you'll need to render the primary key field. For example,
example, you need to render to the primary key field. For example if you were if you were rendering the ``name`` and ``age`` fields of a model::
to render just the ``name`` and ``age`` fields of a model::
<form method="POST" action=""> <form method="POST" action="">
{{ formset.management_form }} {{ formset.management_form }}
@ -611,33 +611,31 @@ to render just the ``name`` and ``age`` fields of a model::
{% endfor %} {% endfor %}
</form> </form>
Notice how we need to explicitly render ``{{ form.id }}``. This will ensure Notice how we need to explicitly render ``{{ form.id }}``. This ensures that
the model formset, in the ``POST`` case, will work correctly. The above the model formset, in the ``POST`` case, will work correctly. (This example
example is assuming a primary key named ``id`` which is the name of the assumes a primary key named ``id``. If you've explicitly defined your own
implicit primary key Django creates for you when one isn't given. If you have primary key that isn't called ``id``, make sure it gets rendered.)
explicitly defined your own primary key field just make sure it gets rendered
(it is likely to be a visible field anyway).
Inline Formsets Inline formsets
=============== ===============
Inline formsets is a small abstraction layer on top of model formsets. It Inline formsets is a small abstraction layer on top of model formsets. These
simplifies the case of working with related objects via a foreign key. Suppose simplify the case of working with related objects via a foreign key. Suppose
you have these two models:: you have these two models::
class Author(models.Model): class Author(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
class Book(models.Model): class Book(models.Model):
author = models.ForeignKey(Author) author = models.ForeignKey(Author)
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
If you want to create a formset that allows you to edit books belonging to If you want to create a formset that allows you to edit books belonging to
some author you might do:: a particular author, you could do this::
>>> from django.forms.models import inlineformset_factory >>> from django.forms.models import inlineformset_factory
>>> BookFormSet = inlineformset_factory(Author, Book) >>> BookFormSet = inlineformset_factory(Author, Book)
>>> author = Author.objects.get(name=u'Orson Scott Card') >>> author = Author.objects.get(name=u'Mike Royko')
>>> formset = BookFormSet(instance=author) >>> formset = BookFormSet(instance=author)
.. note:: .. note::
@ -647,16 +645,16 @@ some author you might do::
More than one foreign key to the same model More than one foreign key to the same model
------------------------------------------- -------------------------------------------
If your model contains more than one foreign key to the same model you will If your model contains more than one foreign key to the same model, you'll
need to resolve the ambiguity manually using ``fk_name``. Given the following need to resolve the ambiguity manually using ``fk_name``. For example, consider
model:: the following model::
class Friendship(models.Model): class Friendship(models.Model):
from_friend = models.ForeignKey(Friend) from_friend = models.ForeignKey(Friend)
to_friend = models.ForeignKey(Friend) to_friend = models.ForeignKey(Friend)
length_in_months = models.IntegerField() length_in_months = models.IntegerField()
To resolve this you can simply use ``fk_name`` to ``inlineformset_factory``:: To resolve this, you can use ``fk_name`` to ``inlineformset_factory``::
>>> FrienshipFormSet = inlineformset_factory(Friend, Friendship, fk_name="from_friend") >>> FrienshipFormSet = inlineformset_factory(Friend, Friendship, fk_name="from_friend")
@ -664,7 +662,7 @@ Using an inline formset in a view
--------------------------------- ---------------------------------
You may want to provide a view that allows a user to edit the related objects You may want to provide a view that allows a user to edit the related objects
of some model. Here is how you might construct this view:: of a model. Here's how you can do that::
def manage_books(request, author_id): def manage_books(request, author_id):
author = Author.objects.get(pk=author_id) author = Author.objects.get(pk=author_id)
@ -673,13 +671,11 @@ of some model. Here is how you might construct this view::
formset = BookInlineFormSet(request.POST, request.FILES, instance=author) formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
if formset.is_valid(): if formset.is_valid():
formset.save() formset.save()
# do something # Do something.
else: else:
formset = BookInlineFormSet(instance=author) formset = BookInlineFormSet(instance=author)
return render_to_response("manage_books.html", { return render_to_response("manage_books.html", {
"formset": formset, "formset": formset,
}) })
Notice how we pass the instance in both the ``POST`` and ``GET`` cases. This Notice how we pass ``instance`` in both the ``POST`` and ``GET`` cases.
is required similiar to model formsets since the ``instance`` is simply used
to create the queryset for the model formset that lives underneath.