From 6553ddc5b41e3225b61739dfd44bb63849d49ac3 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Tue, 9 Dec 2008 00:31:17 +0000 Subject: [PATCH] Improved the model formset and inline formset documentation to be more explicit and handle some cases that were never addressed before. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9614 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/topics/forms/modelforms.txt | 119 ++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 8 deletions(-) diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 49c17fc556..0438b79023 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -530,7 +530,7 @@ formset to a user to edit ``Author`` model instances:: # do something. else: formset = AuthorFormSet() - render_to_response("manage_authors.html", { + return render_to_response("manage_authors.html", { "formset": formset, }) @@ -539,12 +539,91 @@ in a view. The only difference is that we call ``formset.save()`` to save the data into the database. This is described above in :ref:`saving-objects-in-the-formset`. -Using ``inlineformset_factory`` -------------------------------- +Using a custom queryset +~~~~~~~~~~~~~~~~~~~~~~~ -The ``inlineformset_factory`` is a helper to a common usage pattern of working -with related objects through a foreign key. It takes all the same options as -a ``modelformset_factory``. Suppose you have these two models:: +As stated earlier you can override the default queryset the model formset +uses:: + + 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, + }) + +What is critical to point out here is that you must pass the queryset in both +the ``POST`` and ``GET`` cases shown above. + +Using the formset in the template +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are three ways you might want to render the formset in your template. +You can let the formset do most of the work:: + +
+ {{ formset }} +
+ +You can manually render the formset, but let the form deal with it self:: + +
+ {{ formset.management_form }} + {% for form in formset.forms %} + {{ form }} + {% endfor %} +
+ +When you manually render the forms yourself, be sure to render the management +form as shown above. Also see the :ref:`management form documentation `. + +Or you can just do it all yourself:: + +
+ {{ formset.management_form }} + {% for form in formset.formset %} + {% for fields in form %} + {{ field }} + {% endfor %} + {% endfor %} +
+ +It is critical to note that if you opt to do most of the work yourself and you +don't go with a field ``{% for %}`` loop of the form, as shown in the last +example, you need to render to the primary key field. For example if you were +to render just the ``name`` and ``age`` fields of a model:: + +
+ {{ formset.management_form }} + {% for form in formset.formset %} + {{ form.id }} +
    +
  • {{ form.name }}
  • +
  • {{ form.age }}
  • +
+ {% endfor %} +
+ +Notice how we need to explicitly render ``{{ form.id }}``. This will ensure +the model formset, in the ``POST`` case, will work correctly. The above +example is assuming a primary key named ``id`` which is the name of the +implicit primary key Django creates for you when one isn't given. If you have +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 is a small abstraction layer on top of model formsets. It +simplifies the case of working with related objects via a foreign key. Suppose +you have these two models:: class Author(models.Model): name = models.CharField(max_length=100) @@ -554,7 +633,7 @@ a ``modelformset_factory``. Suppose you have these two models:: title = models.CharField(max_length=100) If you want to create a formset that allows you to edit books belonging to -some author you would do:: +some author you might do:: >>> from django.forms.models import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book) @@ -566,7 +645,7 @@ some author you would do:: ``can_delete=True``. More than one foreign key to the same model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------------- If your model contains more than one foreign key to the same model you will need to resolve the ambiguity manually using ``fk_name``. Given the following @@ -580,3 +659,27 @@ model:: To resolve this you can simply use ``fk_name`` to ``inlineformset_factory``:: >>> FrienshipFormSet = 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 some model. Here is how you might construct this view:: + + 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 + else: + formset = BookInlineFormSet(instance=author) + return render_to_response("manage_books.html", { + "formset": formset, + }) + +Notice how we pass the instance in both the ``POST`` and ``GET`` cases. This +is required similiar to model formsets since the ``instance`` is simply used +to create the queryset for the model formset that lives underneath.