|
1 .. _formsets: |
|
2 |
|
3 Formsets |
|
4 ======== |
|
5 |
|
6 A formset is a layer of abstraction to working with multiple forms on the same |
|
7 page. It can be best compared to a data grid. Let's say you have the following |
|
8 form:: |
|
9 |
|
10 >>> from django import forms |
|
11 >>> class ArticleForm(forms.Form): |
|
12 ... title = forms.CharField() |
|
13 ... pub_date = forms.DateField() |
|
14 |
|
15 You might want to allow the user to create several articles at once. To create |
|
16 a formset out of an ``ArticleForm`` you would do:: |
|
17 |
|
18 >>> from django.forms.formsets import formset_factory |
|
19 >>> ArticleFormSet = formset_factory(ArticleForm) |
|
20 |
|
21 You now have created a formset named ``ArticleFormSet``. The formset gives you |
|
22 the ability to iterate over the forms in the formset and display them as you |
|
23 would with a regular form:: |
|
24 |
|
25 >>> formset = ArticleFormSet() |
|
26 >>> for form in formset.forms: |
|
27 ... print form.as_table() |
|
28 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> |
|
29 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> |
|
30 |
|
31 As you can see it only displayed one empty form. The number of empty forms |
|
32 that is displayed is controlled by the ``extra`` parameter. By default, |
|
33 ``formset_factory`` defines one extra form; the following example will |
|
34 display two blank forms:: |
|
35 |
|
36 >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) |
|
37 |
|
38 Using initial data with a formset |
|
39 --------------------------------- |
|
40 |
|
41 Initial data is what drives the main usability of a formset. As shown above |
|
42 you can define the number of extra forms. What this means is that you are |
|
43 telling the formset how many additional forms to show in addition to the |
|
44 number of forms it generates from the initial data. Lets take a look at an |
|
45 example:: |
|
46 |
|
47 >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) |
|
48 >>> formset = ArticleFormSet(initial=[ |
|
49 ... {'title': u'Django is now open source', |
|
50 ... 'pub_date': datetime.date.today()}, |
|
51 ... ]) |
|
52 |
|
53 >>> for form in formset.forms: |
|
54 ... print form.as_table() |
|
55 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr> |
|
56 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr> |
|
57 <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr> |
|
58 <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr> |
|
59 <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> |
|
60 <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> |
|
61 |
|
62 There are now a total of three forms showing above. One for the initial data |
|
63 that was passed in and two extra forms. Also note that we are passing in a |
|
64 list of dictionaries as the initial data. |
|
65 |
|
66 .. seealso:: |
|
67 |
|
68 :ref:`Creating formsets from models with model formsets <model-formsets>`. |
|
69 |
|
70 .. _formsets-max-num: |
|
71 |
|
72 Limiting the maximum number of forms |
|
73 ------------------------------------ |
|
74 |
|
75 The ``max_num`` parameter to ``formset_factory`` gives you the ability to |
|
76 limit the maximum number of empty forms the formset will display:: |
|
77 |
|
78 >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) |
|
79 >>> formset = ArticleFormset() |
|
80 >>> for form in formset.forms: |
|
81 ... print form.as_table() |
|
82 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> |
|
83 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> |
|
84 |
|
85 .. versionchanged:: 1.2 |
|
86 |
|
87 If the value of ``max_num`` is greater than the number of existing |
|
88 objects, up to ``extra`` additional blank forms will be added to the formset, |
|
89 so long as the total number of forms does not exceed ``max_num``. |
|
90 |
|
91 A ``max_num`` value of ``None`` (the default) puts no limit on the number of |
|
92 forms displayed. Please note that the default value of ``max_num`` was changed |
|
93 from ``0`` to ``None`` in version 1.2 to allow ``0`` as a valid value. |
|
94 |
|
95 Formset validation |
|
96 ------------------ |
|
97 |
|
98 Validation with a formset is almost identical to a regular ``Form``. There is |
|
99 an ``is_valid`` method on the formset to provide a convenient way to validate |
|
100 all forms in the formset:: |
|
101 |
|
102 >>> ArticleFormSet = formset_factory(ArticleForm) |
|
103 >>> formset = ArticleFormSet({}) |
|
104 >>> formset.is_valid() |
|
105 True |
|
106 |
|
107 We passed in no data to the formset which is resulting in a valid form. The |
|
108 formset is smart enough to ignore extra forms that were not changed. If we |
|
109 provide an invalid article:: |
|
110 |
|
111 >>> data = { |
|
112 ... 'form-TOTAL_FORMS': u'2', |
|
113 ... 'form-INITIAL_FORMS': u'0', |
|
114 ... 'form-MAX_NUM_FORMS': u'', |
|
115 ... 'form-0-title': u'Test', |
|
116 ... 'form-0-pub_date': u'16 June 1904', |
|
117 ... 'form-1-title': u'Test', |
|
118 ... 'form-1-pub_date': u'', # <-- this date is missing but required |
|
119 ... } |
|
120 >>> formset = ArticleFormSet(data) |
|
121 >>> formset.is_valid() |
|
122 False |
|
123 >>> formset.errors |
|
124 [{}, {'pub_date': [u'This field is required.']}] |
|
125 |
|
126 As we can see, ``formset.errors`` is a list whose entries correspond to the |
|
127 forms in the formset. Validation was performed for each of the two forms, and |
|
128 the expected error message appears for the second item. |
|
129 |
|
130 .. _understanding-the-managementform: |
|
131 |
|
132 Understanding the ManagementForm |
|
133 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
134 |
|
135 You may have noticed the additional data (``form-TOTAL_FORMS``, |
|
136 ``form-INITIAL_FORMS`` and ``form-MAX_NUM_FORMS``) that was required |
|
137 in the formset's data above. This data is required for the |
|
138 ``ManagementForm``. This form is used by the formset to manage the |
|
139 collection of forms contained in the formset. If you don't provide |
|
140 this management data, an exception will be raised:: |
|
141 |
|
142 >>> data = { |
|
143 ... 'form-0-title': u'Test', |
|
144 ... 'form-0-pub_date': u'', |
|
145 ... } |
|
146 >>> formset = ArticleFormSet(data) |
|
147 Traceback (most recent call last): |
|
148 ... |
|
149 django.forms.util.ValidationError: [u'ManagementForm data is missing or has been tampered with'] |
|
150 |
|
151 It is used to keep track of how many form instances are being displayed. If |
|
152 you are adding new forms via JavaScript, you should increment the count fields |
|
153 in this form as well. |
|
154 |
|
155 The management form is available as an attribute of the formset |
|
156 itself. When rendering a formset in a template, you can include all |
|
157 the management data by rendering ``{{ my_formset.management_form }}`` |
|
158 (substituting the name of your formset as appropriate). |
|
159 |
|
160 .. versionadded:: 1.1 |
|
161 |
|
162 ``total_form_count`` and ``initial_form_count`` |
|
163 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
164 |
|
165 ``BaseFormSet`` has a couple of methods that are closely related to the |
|
166 ``ManagementForm``, ``total_form_count`` and ``initial_form_count``. |
|
167 |
|
168 ``total_form_count`` returns the total number of forms in this formset. |
|
169 ``initial_form_count`` returns the number of forms in the formset that were |
|
170 pre-filled, and is also used to determine how many forms are required. You |
|
171 will probably never need to override either of these methods, so please be |
|
172 sure you understand what they do before doing so. |
|
173 |
|
174 .. versionadded:: 1.2 |
|
175 |
|
176 ``empty_form`` |
|
177 ~~~~~~~~~~~~~~ |
|
178 |
|
179 ``BaseFormSet`` provides an additional attribute ``empty_form`` which returns |
|
180 a form instance with a prefix of ``__prefix__`` for easier use in dynamic |
|
181 forms with JavaScript. |
|
182 |
|
183 Custom formset validation |
|
184 ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
185 |
|
186 A formset has a ``clean`` method similar to the one on a ``Form`` class. This |
|
187 is where you define your own validation that works at the formset level:: |
|
188 |
|
189 >>> from django.forms.formsets import BaseFormSet |
|
190 |
|
191 >>> class BaseArticleFormSet(BaseFormSet): |
|
192 ... def clean(self): |
|
193 ... """Checks that no two articles have the same title.""" |
|
194 ... if any(self.errors): |
|
195 ... # Don't bother validating the formset unless each form is valid on its own |
|
196 ... return |
|
197 ... titles = [] |
|
198 ... for i in range(0, self.total_form_count()): |
|
199 ... form = self.forms[i] |
|
200 ... title = form.cleaned_data['title'] |
|
201 ... if title in titles: |
|
202 ... raise forms.ValidationError, "Articles in a set must have distinct titles." |
|
203 ... titles.append(title) |
|
204 |
|
205 >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) |
|
206 >>> data = { |
|
207 ... 'form-TOTAL_FORMS': u'2', |
|
208 ... 'form-INITIAL_FORMS': u'0', |
|
209 ... 'form-MAX_NUM_FORMS': u'', |
|
210 ... 'form-0-title': u'Test', |
|
211 ... 'form-0-pub_date': u'16 June 1904', |
|
212 ... 'form-1-title': u'Test', |
|
213 ... 'form-1-pub_date': u'23 June 1912', |
|
214 ... } |
|
215 >>> formset = ArticleFormSet(data) |
|
216 >>> formset.is_valid() |
|
217 False |
|
218 >>> formset.errors |
|
219 [{}, {}] |
|
220 >>> formset.non_form_errors() |
|
221 [u'Articles in a set must have distinct titles.'] |
|
222 |
|
223 The formset ``clean`` method is called after all the ``Form.clean`` methods |
|
224 have been called. The errors will be found using the ``non_form_errors()`` |
|
225 method on the formset. |
|
226 |
|
227 Dealing with ordering and deletion of forms |
|
228 ------------------------------------------- |
|
229 |
|
230 Common use cases with a formset is dealing with ordering and deletion of the |
|
231 form instances. This has been dealt with for you. The ``formset_factory`` |
|
232 provides two optional parameters ``can_order`` and ``can_delete`` that will do |
|
233 the extra work of adding the extra fields and providing simpler ways of |
|
234 getting to that data. |
|
235 |
|
236 ``can_order`` |
|
237 ~~~~~~~~~~~~~ |
|
238 |
|
239 Default: ``False`` |
|
240 |
|
241 Lets create a formset with the ability to order:: |
|
242 |
|
243 >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) |
|
244 >>> formset = ArticleFormSet(initial=[ |
|
245 ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, |
|
246 ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, |
|
247 ... ]) |
|
248 >>> for form in formset.forms: |
|
249 ... print form.as_table() |
|
250 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> |
|
251 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> |
|
252 <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> |
|
253 <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> |
|
254 <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> |
|
255 <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> |
|
256 <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> |
|
257 <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> |
|
258 <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr> |
|
259 |
|
260 This adds an additional field to each form. This new field is named ``ORDER`` |
|
261 and is an ``forms.IntegerField``. For the forms that came from the initial |
|
262 data it automatically assigned them a numeric value. Lets look at what will |
|
263 happen when the user changes these values:: |
|
264 |
|
265 >>> data = { |
|
266 ... 'form-TOTAL_FORMS': u'3', |
|
267 ... 'form-INITIAL_FORMS': u'2', |
|
268 ... 'form-MAX_NUM_FORMS': u'', |
|
269 ... 'form-0-title': u'Article #1', |
|
270 ... 'form-0-pub_date': u'2008-05-10', |
|
271 ... 'form-0-ORDER': u'2', |
|
272 ... 'form-1-title': u'Article #2', |
|
273 ... 'form-1-pub_date': u'2008-05-11', |
|
274 ... 'form-1-ORDER': u'1', |
|
275 ... 'form-2-title': u'Article #3', |
|
276 ... 'form-2-pub_date': u'2008-05-01', |
|
277 ... 'form-2-ORDER': u'0', |
|
278 ... } |
|
279 |
|
280 >>> formset = ArticleFormSet(data, initial=[ |
|
281 ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, |
|
282 ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, |
|
283 ... ]) |
|
284 >>> formset.is_valid() |
|
285 True |
|
286 >>> for form in formset.ordered_forms: |
|
287 ... print form.cleaned_data |
|
288 {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': u'Article #3'} |
|
289 {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': u'Article #2'} |
|
290 {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': u'Article #1'} |
|
291 |
|
292 ``can_delete`` |
|
293 ~~~~~~~~~~~~~~ |
|
294 |
|
295 Default: ``False`` |
|
296 |
|
297 Lets create a formset with the ability to delete:: |
|
298 |
|
299 >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) |
|
300 >>> formset = ArticleFormSet(initial=[ |
|
301 ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, |
|
302 ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, |
|
303 ... ]) |
|
304 >>> for form in formset.forms: |
|
305 .... print form.as_table() |
|
306 <input type="hidden" name="form-TOTAL_FORMS" value="3" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS" /><input type="hidden" name="form-MAX_NUM_FORMS" id="id_form-MAX_NUM_FORMS" /> |
|
307 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> |
|
308 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> |
|
309 <tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr> |
|
310 <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> |
|
311 <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> |
|
312 <tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr> |
|
313 <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> |
|
314 <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> |
|
315 <tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr> |
|
316 |
|
317 Similar to ``can_order`` this adds a new field to each form named ``DELETE`` |
|
318 and is a ``forms.BooleanField``. When data comes through marking any of the |
|
319 delete fields you can access them with ``deleted_forms``:: |
|
320 |
|
321 >>> data = { |
|
322 ... 'form-TOTAL_FORMS': u'3', |
|
323 ... 'form-INITIAL_FORMS': u'2', |
|
324 ... 'form-MAX_NUM_FORMS': u'', |
|
325 ... 'form-0-title': u'Article #1', |
|
326 ... 'form-0-pub_date': u'2008-05-10', |
|
327 ... 'form-0-DELETE': u'on', |
|
328 ... 'form-1-title': u'Article #2', |
|
329 ... 'form-1-pub_date': u'2008-05-11', |
|
330 ... 'form-1-DELETE': u'', |
|
331 ... 'form-2-title': u'', |
|
332 ... 'form-2-pub_date': u'', |
|
333 ... 'form-2-DELETE': u'', |
|
334 ... } |
|
335 |
|
336 >>> formset = ArticleFormSet(data, initial=[ |
|
337 ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, |
|
338 ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, |
|
339 ... ]) |
|
340 >>> [form.cleaned_data for form in formset.deleted_forms] |
|
341 [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}] |
|
342 |
|
343 Adding additional fields to a formset |
|
344 ------------------------------------- |
|
345 |
|
346 If you need to add additional fields to the formset this can be easily |
|
347 accomplished. The formset base class provides an ``add_fields`` method. You |
|
348 can simply override this method to add your own fields or even redefine the |
|
349 default fields/attributes of the order and deletion fields:: |
|
350 |
|
351 >>> class BaseArticleFormSet(BaseFormSet): |
|
352 ... def add_fields(self, form, index): |
|
353 ... super(BaseArticleFormSet, self).add_fields(form, index) |
|
354 ... form.fields["my_field"] = forms.CharField() |
|
355 |
|
356 >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) |
|
357 >>> formset = ArticleFormSet() |
|
358 >>> for form in formset.forms: |
|
359 ... print form.as_table() |
|
360 <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> |
|
361 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> |
|
362 <tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr> |
|
363 |
|
364 Using a formset in views and templates |
|
365 -------------------------------------- |
|
366 |
|
367 Using a formset inside a view is as easy as using a regular ``Form`` class. |
|
368 The only thing you will want to be aware of is making sure to use the |
|
369 management form inside the template. Let's look at a sample view: |
|
370 |
|
371 .. code-block:: python |
|
372 |
|
373 def manage_articles(request): |
|
374 ArticleFormSet = formset_factory(ArticleForm) |
|
375 if request.method == 'POST': |
|
376 formset = ArticleFormSet(request.POST, request.FILES) |
|
377 if formset.is_valid(): |
|
378 # do something with the formset.cleaned_data |
|
379 pass |
|
380 else: |
|
381 formset = ArticleFormSet() |
|
382 return render_to_response('manage_articles.html', {'formset': formset}) |
|
383 |
|
384 The ``manage_articles.html`` template might look like this: |
|
385 |
|
386 .. code-block:: html+django |
|
387 |
|
388 <form method="post" action=""> |
|
389 {{ formset.management_form }} |
|
390 <table> |
|
391 {% for form in formset.forms %} |
|
392 {{ form }} |
|
393 {% endfor %} |
|
394 </table> |
|
395 </form> |
|
396 |
|
397 However the above can be slightly shortcutted and let the formset itself deal |
|
398 with the management form: |
|
399 |
|
400 .. code-block:: html+django |
|
401 |
|
402 <form method="post" action=""> |
|
403 <table> |
|
404 {{ formset }} |
|
405 </table> |
|
406 </form> |
|
407 |
|
408 The above ends up calling the ``as_table`` method on the formset class. |
|
409 |
|
410 Using more than one formset in a view |
|
411 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
412 |
|
413 You are able to use more than one formset in a view if you like. Formsets |
|
414 borrow much of its behavior from forms. With that said you are able to use |
|
415 ``prefix`` to prefix formset form field names with a given value to allow |
|
416 more than one formset to be sent to a view without name clashing. Lets take |
|
417 a look at how this might be accomplished: |
|
418 |
|
419 .. code-block:: python |
|
420 |
|
421 def manage_articles(request): |
|
422 ArticleFormSet = formset_factory(ArticleForm) |
|
423 BookFormSet = formset_factory(BookForm) |
|
424 if request.method == 'POST': |
|
425 article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles') |
|
426 book_formset = BookFormSet(request.POST, request.FILES, prefix='books') |
|
427 if article_formset.is_valid() and book_formset.is_valid(): |
|
428 # do something with the cleaned_data on the formsets. |
|
429 pass |
|
430 else: |
|
431 article_formset = ArticleFormSet(prefix='articles') |
|
432 book_formset = BookFormSet(prefix='books') |
|
433 return render_to_response('manage_articles.html', { |
|
434 'article_formset': article_formset, |
|
435 'book_formset': book_formset, |
|
436 }) |
|
437 |
|
438 You would then render the formsets as normal. It is important to point out |
|
439 that you need to pass ``prefix`` on both the POST and non-POST cases so that |
|
440 it is rendered and processed correctly. |