|
1 import datetime |
|
2 import re |
|
3 from datetime import date |
|
4 from decimal import Decimal |
|
5 |
|
6 from django import forms |
|
7 from django.db import models |
|
8 from django.forms.models import (_get_foreign_key, inlineformset_factory, |
|
9 modelformset_factory, modelformset_factory) |
|
10 from django.test import TestCase |
|
11 |
|
12 from modeltests.model_formsets.models import ( |
|
13 Author, BetterAuthor, Book, BookWithCustomPK, Editor, |
|
14 BookWithOptionalAltEditor, AlternateBook, AuthorMeeting, CustomPrimaryKey, |
|
15 Place, Owner, Location, OwnerProfile, Restaurant, Product, Price, |
|
16 MexicanRestaurant, ClassyMexicanRestaurant, Repository, Revision, |
|
17 Person, Membership, Team, Player, Poet, Poem, Post) |
|
18 |
|
19 class DeletionTests(TestCase): |
|
20 def test_deletion(self): |
|
21 PoetFormSet = modelformset_factory(Poet, can_delete=True) |
|
22 poet = Poet.objects.create(name='test') |
|
23 data = { |
|
24 'form-TOTAL_FORMS': u'1', |
|
25 'form-INITIAL_FORMS': u'1', |
|
26 'form-MAX_NUM_FORMS': u'0', |
|
27 'form-0-id': str(poet.pk), |
|
28 'form-0-name': u'test', |
|
29 'form-0-DELETE': u'on', |
|
30 } |
|
31 formset = PoetFormSet(data, queryset=Poet.objects.all()) |
|
32 formset.save() |
|
33 self.assertTrue(formset.is_valid()) |
|
34 self.assertEqual(Poet.objects.count(), 0) |
|
35 |
|
36 def test_add_form_deletion_when_invalid(self): |
|
37 """ |
|
38 Make sure that an add form that is filled out, but marked for deletion |
|
39 doesn't cause validation errors. |
|
40 """ |
|
41 PoetFormSet = modelformset_factory(Poet, can_delete=True) |
|
42 data = { |
|
43 'form-TOTAL_FORMS': u'1', |
|
44 'form-INITIAL_FORMS': u'0', |
|
45 'form-MAX_NUM_FORMS': u'0', |
|
46 'form-0-id': u'', |
|
47 'form-0-name': u'x' * 1000, |
|
48 } |
|
49 formset = PoetFormSet(data, queryset=Poet.objects.all()) |
|
50 # Make sure this form doesn't pass validation. |
|
51 self.assertEqual(formset.is_valid(), False) |
|
52 self.assertEqual(Poet.objects.count(), 0) |
|
53 |
|
54 # Then make sure that it *does* pass validation and delete the object, |
|
55 # even though the data isn't actually valid. |
|
56 data['form-0-DELETE'] = 'on' |
|
57 formset = PoetFormSet(data, queryset=Poet.objects.all()) |
|
58 self.assertEqual(formset.is_valid(), True) |
|
59 formset.save() |
|
60 self.assertEqual(Poet.objects.count(), 0) |
|
61 |
|
62 def test_change_form_deletion_when_invalid(self): |
|
63 """ |
|
64 Make sure that an add form that is filled out, but marked for deletion |
|
65 doesn't cause validation errors. |
|
66 """ |
|
67 PoetFormSet = modelformset_factory(Poet, can_delete=True) |
|
68 poet = Poet.objects.create(name='test') |
|
69 data = { |
|
70 'form-TOTAL_FORMS': u'1', |
|
71 'form-INITIAL_FORMS': u'1', |
|
72 'form-MAX_NUM_FORMS': u'0', |
|
73 'form-0-id': u'1', |
|
74 'form-0-name': u'x' * 1000, |
|
75 } |
|
76 formset = PoetFormSet(data, queryset=Poet.objects.all()) |
|
77 # Make sure this form doesn't pass validation. |
|
78 self.assertEqual(formset.is_valid(), False) |
|
79 self.assertEqual(Poet.objects.count(), 1) |
|
80 |
|
81 # Then make sure that it *does* pass validation and delete the object, |
|
82 # even though the data isn't actually valid. |
|
83 data['form-0-DELETE'] = 'on' |
|
84 formset = PoetFormSet(data, queryset=Poet.objects.all()) |
|
85 self.assertEqual(formset.is_valid(), True) |
|
86 formset.save() |
|
87 self.assertEqual(Poet.objects.count(), 0) |
|
88 |
|
89 class ModelFormsetTest(TestCase): |
|
90 def test_simple_save(self): |
|
91 qs = Author.objects.all() |
|
92 AuthorFormSet = modelformset_factory(Author, extra=3) |
|
93 |
|
94 formset = AuthorFormSet(queryset=qs) |
|
95 self.assertEqual(len(formset.forms), 3) |
|
96 self.assertEqual(formset.forms[0].as_p(), |
|
97 '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></p>') |
|
98 self.assertEqual(formset.forms[1].as_p(), |
|
99 '<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /><input type="hidden" name="form-1-id" id="id_form-1-id" /></p>') |
|
100 self.assertEqual(formset.forms[2].as_p(), |
|
101 '<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>') |
|
102 |
|
103 data = { |
|
104 'form-TOTAL_FORMS': '3', # the number of forms rendered |
|
105 'form-INITIAL_FORMS': '0', # the number of forms with initial data |
|
106 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
107 'form-0-name': 'Charles Baudelaire', |
|
108 'form-1-name': 'Arthur Rimbaud', |
|
109 'form-2-name': '', |
|
110 } |
|
111 |
|
112 formset = AuthorFormSet(data=data, queryset=qs) |
|
113 self.assertTrue(formset.is_valid()) |
|
114 |
|
115 saved = formset.save() |
|
116 self.assertEqual(len(saved), 2) |
|
117 author1, author2 = saved |
|
118 self.assertEqual(author1, Author.objects.get(name='Charles Baudelaire')) |
|
119 self.assertEqual(author2, Author.objects.get(name='Arthur Rimbaud')) |
|
120 |
|
121 authors = list(Author.objects.order_by('name')) |
|
122 self.assertEqual(authors, [author2, author1]) |
|
123 |
|
124 # Gah! We forgot Paul Verlaine. Let's create a formset to edit the |
|
125 # existing authors with an extra form to add him. We *could* pass in a |
|
126 # queryset to restrict the Author objects we edit, but in this case |
|
127 # we'll use it to display them in alphabetical order by name. |
|
128 |
|
129 qs = Author.objects.order_by('name') |
|
130 AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=False) |
|
131 |
|
132 formset = AuthorFormSet(queryset=qs) |
|
133 self.assertEqual(len(formset.forms), 3) |
|
134 self.assertEqual(formset.forms[0].as_p(), |
|
135 '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /><input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /></p>' % author2.id) |
|
136 self.assertEqual(formset.forms[1].as_p(), |
|
137 '<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" /></p>' % author1.id) |
|
138 self.assertEqual(formset.forms[2].as_p(), |
|
139 '<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" maxlength="100" /><input type="hidden" name="form-2-id" id="id_form-2-id" /></p>') |
|
140 |
|
141 data = { |
|
142 'form-TOTAL_FORMS': '3', # the number of forms rendered |
|
143 'form-INITIAL_FORMS': '2', # the number of forms with initial data |
|
144 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
145 'form-0-id': str(author2.id), |
|
146 'form-0-name': 'Arthur Rimbaud', |
|
147 'form-1-id': str(author1.id), |
|
148 'form-1-name': 'Charles Baudelaire', |
|
149 'form-2-name': 'Paul Verlaine', |
|
150 } |
|
151 |
|
152 formset = AuthorFormSet(data=data, queryset=qs) |
|
153 self.assertTrue(formset.is_valid()) |
|
154 |
|
155 # Only changed or new objects are returned from formset.save() |
|
156 saved = formset.save() |
|
157 self.assertEqual(len(saved), 1) |
|
158 author3 = saved[0] |
|
159 self.assertEqual(author3, Author.objects.get(name='Paul Verlaine')) |
|
160 |
|
161 authors = list(Author.objects.order_by('name')) |
|
162 self.assertEqual(authors, [author2, author1, author3]) |
|
163 |
|
164 # This probably shouldn't happen, but it will. If an add form was |
|
165 # marked for deletion, make sure we don't save that form. |
|
166 |
|
167 qs = Author.objects.order_by('name') |
|
168 AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=True) |
|
169 |
|
170 formset = AuthorFormSet(queryset=qs) |
|
171 self.assertEqual(len(formset.forms), 4) |
|
172 self.assertEqual(formset.forms[0].as_p(), |
|
173 '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /></p>\n' |
|
174 '<p><label for="id_form-0-DELETE">Delete:</label> <input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /><input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /></p>' % author2.id) |
|
175 self.assertEqual(formset.forms[1].as_p(), |
|
176 '<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" value="Charles Baudelaire" maxlength="100" /></p>\n' |
|
177 '<p><label for="id_form-1-DELETE">Delete:</label> <input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" /></p>' % author1.id) |
|
178 self.assertEqual(formset.forms[2].as_p(), |
|
179 '<p><label for="id_form-2-name">Name:</label> <input id="id_form-2-name" type="text" name="form-2-name" value="Paul Verlaine" maxlength="100" /></p>\n' |
|
180 '<p><label for="id_form-2-DELETE">Delete:</label> <input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /><input type="hidden" name="form-2-id" value="%d" id="id_form-2-id" /></p>' % author3.id) |
|
181 self.assertEqual(formset.forms[3].as_p(), |
|
182 '<p><label for="id_form-3-name">Name:</label> <input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /></p>\n' |
|
183 '<p><label for="id_form-3-DELETE">Delete:</label> <input type="checkbox" name="form-3-DELETE" id="id_form-3-DELETE" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></p>') |
|
184 |
|
185 data = { |
|
186 'form-TOTAL_FORMS': '4', # the number of forms rendered |
|
187 'form-INITIAL_FORMS': '3', # the number of forms with initial data |
|
188 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
189 'form-0-id': str(author2.id), |
|
190 'form-0-name': 'Arthur Rimbaud', |
|
191 'form-1-id': str(author1.id), |
|
192 'form-1-name': 'Charles Baudelaire', |
|
193 'form-2-id': str(author3.id), |
|
194 'form-2-name': 'Paul Verlaine', |
|
195 'form-3-name': 'Walt Whitman', |
|
196 'form-3-DELETE': 'on', |
|
197 } |
|
198 |
|
199 formset = AuthorFormSet(data=data, queryset=qs) |
|
200 self.assertTrue(formset.is_valid()) |
|
201 |
|
202 # No objects were changed or saved so nothing will come back. |
|
203 |
|
204 self.assertEqual(formset.save(), []) |
|
205 |
|
206 authors = list(Author.objects.order_by('name')) |
|
207 self.assertEqual(authors, [author2, author1, author3]) |
|
208 |
|
209 # Let's edit a record to ensure save only returns that one record. |
|
210 |
|
211 data = { |
|
212 'form-TOTAL_FORMS': '4', # the number of forms rendered |
|
213 'form-INITIAL_FORMS': '3', # the number of forms with initial data |
|
214 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
215 'form-0-id': str(author2.id), |
|
216 'form-0-name': 'Walt Whitman', |
|
217 'form-1-id': str(author1.id), |
|
218 'form-1-name': 'Charles Baudelaire', |
|
219 'form-2-id': str(author3.id), |
|
220 'form-2-name': 'Paul Verlaine', |
|
221 'form-3-name': '', |
|
222 'form-3-DELETE': '', |
|
223 } |
|
224 |
|
225 formset = AuthorFormSet(data=data, queryset=qs) |
|
226 self.assertTrue(formset.is_valid()) |
|
227 |
|
228 # One record has changed. |
|
229 |
|
230 saved = formset.save() |
|
231 self.assertEqual(len(saved), 1) |
|
232 self.assertEqual(saved[0], Author.objects.get(name='Walt Whitman')) |
|
233 |
|
234 def test_commit_false(self): |
|
235 # Test the behavior of commit=False and save_m2m |
|
236 |
|
237 author1 = Author.objects.create(name='Charles Baudelaire') |
|
238 author2 = Author.objects.create(name='Paul Verlaine') |
|
239 author3 = Author.objects.create(name='Walt Whitman') |
|
240 |
|
241 meeting = AuthorMeeting.objects.create(created=date.today()) |
|
242 meeting.authors = Author.objects.all() |
|
243 |
|
244 # create an Author instance to add to the meeting. |
|
245 |
|
246 author4 = Author.objects.create(name=u'John Steinbeck') |
|
247 |
|
248 AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True) |
|
249 data = { |
|
250 'form-TOTAL_FORMS': '2', # the number of forms rendered |
|
251 'form-INITIAL_FORMS': '1', # the number of forms with initial data |
|
252 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
253 'form-0-id': '1', |
|
254 'form-0-name': '2nd Tuesday of the Week Meeting', |
|
255 'form-0-authors': [2, 1, 3, 4], |
|
256 'form-1-name': '', |
|
257 'form-1-authors': '', |
|
258 'form-1-DELETE': '', |
|
259 } |
|
260 formset = AuthorMeetingFormSet(data=data, queryset=AuthorMeeting.objects.all()) |
|
261 self.assertTrue(formset.is_valid()) |
|
262 |
|
263 instances = formset.save(commit=False) |
|
264 for instance in instances: |
|
265 instance.created = date.today() |
|
266 instance.save() |
|
267 formset.save_m2m() |
|
268 self.assertQuerysetEqual(instances[0].authors.all(), [ |
|
269 '<Author: Charles Baudelaire>', |
|
270 '<Author: John Steinbeck>', |
|
271 '<Author: Paul Verlaine>', |
|
272 '<Author: Walt Whitman>', |
|
273 ]) |
|
274 |
|
275 def test_max_num(self): |
|
276 # Test the behavior of max_num with model formsets. It should allow |
|
277 # all existing related objects/inlines for a given object to be |
|
278 # displayed, but not allow the creation of new inlines beyond max_num. |
|
279 |
|
280 author1 = Author.objects.create(name='Charles Baudelaire') |
|
281 author2 = Author.objects.create(name='Paul Verlaine') |
|
282 author3 = Author.objects.create(name='Walt Whitman') |
|
283 |
|
284 qs = Author.objects.order_by('name') |
|
285 |
|
286 AuthorFormSet = modelformset_factory(Author, max_num=None, extra=3) |
|
287 formset = AuthorFormSet(queryset=qs) |
|
288 self.assertEqual(len(formset.forms), 6) |
|
289 self.assertEqual(len(formset.extra_forms), 3) |
|
290 |
|
291 AuthorFormSet = modelformset_factory(Author, max_num=4, extra=3) |
|
292 formset = AuthorFormSet(queryset=qs) |
|
293 self.assertEqual(len(formset.forms), 4) |
|
294 self.assertEqual(len(formset.extra_forms), 1) |
|
295 |
|
296 AuthorFormSet = modelformset_factory(Author, max_num=0, extra=3) |
|
297 formset = AuthorFormSet(queryset=qs) |
|
298 self.assertEqual(len(formset.forms), 3) |
|
299 self.assertEqual(len(formset.extra_forms), 0) |
|
300 |
|
301 AuthorFormSet = modelformset_factory(Author, max_num=None) |
|
302 formset = AuthorFormSet(queryset=qs) |
|
303 self.assertQuerysetEqual(formset.get_queryset(), [ |
|
304 '<Author: Charles Baudelaire>', |
|
305 '<Author: Paul Verlaine>', |
|
306 '<Author: Walt Whitman>', |
|
307 ]) |
|
308 |
|
309 AuthorFormSet = modelformset_factory(Author, max_num=0) |
|
310 formset = AuthorFormSet(queryset=qs) |
|
311 self.assertQuerysetEqual(formset.get_queryset(), [ |
|
312 '<Author: Charles Baudelaire>', |
|
313 '<Author: Paul Verlaine>', |
|
314 '<Author: Walt Whitman>', |
|
315 ]) |
|
316 |
|
317 AuthorFormSet = modelformset_factory(Author, max_num=4) |
|
318 formset = AuthorFormSet(queryset=qs) |
|
319 self.assertQuerysetEqual(formset.get_queryset(), [ |
|
320 '<Author: Charles Baudelaire>', |
|
321 '<Author: Paul Verlaine>', |
|
322 '<Author: Walt Whitman>', |
|
323 ]) |
|
324 |
|
325 def test_custom_save_method(self): |
|
326 class PoetForm(forms.ModelForm): |
|
327 def save(self, commit=True): |
|
328 # change the name to "Vladimir Mayakovsky" just to be a jerk. |
|
329 author = super(PoetForm, self).save(commit=False) |
|
330 author.name = u"Vladimir Mayakovsky" |
|
331 if commit: |
|
332 author.save() |
|
333 return author |
|
334 |
|
335 PoetFormSet = modelformset_factory(Poet, form=PoetForm) |
|
336 |
|
337 data = { |
|
338 'form-TOTAL_FORMS': '3', # the number of forms rendered |
|
339 'form-INITIAL_FORMS': '0', # the number of forms with initial data |
|
340 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
341 'form-0-name': 'Walt Whitman', |
|
342 'form-1-name': 'Charles Baudelaire', |
|
343 'form-2-name': '', |
|
344 } |
|
345 |
|
346 qs = Poet.objects.all() |
|
347 formset = PoetFormSet(data=data, queryset=qs) |
|
348 self.assertTrue(formset.is_valid()) |
|
349 |
|
350 poets = formset.save() |
|
351 self.assertEqual(len(poets), 2) |
|
352 poet1, poet2 = poets |
|
353 self.assertEqual(poet1.name, 'Vladimir Mayakovsky') |
|
354 self.assertEqual(poet2.name, 'Vladimir Mayakovsky') |
|
355 |
|
356 def test_model_inheritance(self): |
|
357 BetterAuthorFormSet = modelformset_factory(BetterAuthor) |
|
358 formset = BetterAuthorFormSet() |
|
359 self.assertEqual(len(formset.forms), 1) |
|
360 self.assertEqual(formset.forms[0].as_p(), |
|
361 '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>\n' |
|
362 '<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr" /></p>') |
|
363 |
|
364 data = { |
|
365 'form-TOTAL_FORMS': '1', # the number of forms rendered |
|
366 'form-INITIAL_FORMS': '0', # the number of forms with initial data |
|
367 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
368 'form-0-author_ptr': '', |
|
369 'form-0-name': 'Ernest Hemingway', |
|
370 'form-0-write_speed': '10', |
|
371 } |
|
372 |
|
373 formset = BetterAuthorFormSet(data) |
|
374 self.assertTrue(formset.is_valid()) |
|
375 saved = formset.save() |
|
376 self.assertEqual(len(saved), 1) |
|
377 author1, = saved |
|
378 self.assertEqual(author1, BetterAuthor.objects.get(name='Ernest Hemingway')) |
|
379 hemingway_id = BetterAuthor.objects.get(name="Ernest Hemingway").pk |
|
380 |
|
381 formset = BetterAuthorFormSet() |
|
382 self.assertEqual(len(formset.forms), 2) |
|
383 self.assertEqual(formset.forms[0].as_p(), |
|
384 '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>\n' |
|
385 '<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" value="%d" id="id_form-0-author_ptr" /></p>' % hemingway_id) |
|
386 self.assertEqual(formset.forms[1].as_p(), |
|
387 '<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>\n' |
|
388 '<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr" /></p>') |
|
389 |
|
390 data = { |
|
391 'form-TOTAL_FORMS': '2', # the number of forms rendered |
|
392 'form-INITIAL_FORMS': '1', # the number of forms with initial data |
|
393 'form-MAX_NUM_FORMS': '', # the max number of forms |
|
394 'form-0-author_ptr': hemingway_id, |
|
395 'form-0-name': 'Ernest Hemingway', |
|
396 'form-0-write_speed': '10', |
|
397 'form-1-author_ptr': '', |
|
398 'form-1-name': '', |
|
399 'form-1-write_speed': '', |
|
400 } |
|
401 |
|
402 formset = BetterAuthorFormSet(data) |
|
403 self.assertTrue(formset.is_valid()) |
|
404 self.assertEqual(formset.save(), []) |
|
405 |
|
406 def test_inline_formsets(self): |
|
407 # We can also create a formset that is tied to a parent model. This is |
|
408 # how the admin system's edit inline functionality works. |
|
409 |
|
410 AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3) |
|
411 author = Author.objects.create(name='Charles Baudelaire') |
|
412 |
|
413 formset = AuthorBooksFormSet(instance=author) |
|
414 self.assertEqual(len(formset.forms), 3) |
|
415 self.assertEqual(formset.forms[0].as_p(), |
|
416 '<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /><input type="hidden" name="book_set-0-author" value="%d" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p>' % author.id) |
|
417 self.assertEqual(formset.forms[1].as_p(), |
|
418 '<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="%d" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>' % author.id) |
|
419 self.assertEqual(formset.forms[2].as_p(), |
|
420 '<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="%d" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>' % author.id) |
|
421 |
|
422 data = { |
|
423 'book_set-TOTAL_FORMS': '3', # the number of forms rendered |
|
424 'book_set-INITIAL_FORMS': '0', # the number of forms with initial data |
|
425 'book_set-MAX_NUM_FORMS': '', # the max number of forms |
|
426 'book_set-0-title': 'Les Fleurs du Mal', |
|
427 'book_set-1-title': '', |
|
428 'book_set-2-title': '', |
|
429 } |
|
430 |
|
431 formset = AuthorBooksFormSet(data, instance=author) |
|
432 self.assertTrue(formset.is_valid()) |
|
433 |
|
434 saved = formset.save() |
|
435 self.assertEqual(len(saved), 1) |
|
436 book1, = saved |
|
437 self.assertEqual(book1, Book.objects.get(title='Les Fleurs du Mal')) |
|
438 self.assertQuerysetEqual(author.book_set.all(), ['<Book: Les Fleurs du Mal>']) |
|
439 |
|
440 # Now that we've added a book to Charles Baudelaire, let's try adding |
|
441 # another one. This time though, an edit form will be available for |
|
442 # every existing book. |
|
443 |
|
444 AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) |
|
445 author = Author.objects.get(name='Charles Baudelaire') |
|
446 |
|
447 formset = AuthorBooksFormSet(instance=author) |
|
448 self.assertEqual(len(formset.forms), 3) |
|
449 self.assertEqual(formset.forms[0].as_p(), |
|
450 '<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0-author" value="%d" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="%d" id="id_book_set-0-id" /></p>' % (author.id, book1.id)) |
|
451 self.assertEqual(formset.forms[1].as_p(), |
|
452 '<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="%d" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>' % author.id) |
|
453 self.assertEqual(formset.forms[2].as_p(), |
|
454 '<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="%d" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>' % author.id) |
|
455 |
|
456 data = { |
|
457 'book_set-TOTAL_FORMS': '3', # the number of forms rendered |
|
458 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data |
|
459 'book_set-MAX_NUM_FORMS': '', # the max number of forms |
|
460 'book_set-0-id': '1', |
|
461 'book_set-0-title': 'Les Fleurs du Mal', |
|
462 'book_set-1-title': 'Les Paradis Artificiels', |
|
463 'book_set-2-title': '', |
|
464 } |
|
465 |
|
466 formset = AuthorBooksFormSet(data, instance=author) |
|
467 self.assertTrue(formset.is_valid()) |
|
468 |
|
469 saved = formset.save() |
|
470 self.assertEqual(len(saved), 1) |
|
471 book2, = saved |
|
472 self.assertEqual(book2, Book.objects.get(title='Les Paradis Artificiels')) |
|
473 |
|
474 # As you can see, 'Les Paradis Artificiels' is now a book belonging to |
|
475 # Charles Baudelaire. |
|
476 self.assertQuerysetEqual(author.book_set.order_by('title'), [ |
|
477 '<Book: Les Fleurs du Mal>', |
|
478 '<Book: Les Paradis Artificiels>', |
|
479 ]) |
|
480 |
|
481 def test_inline_formsets_save_as_new(self): |
|
482 # The save_as_new parameter lets you re-associate the data to a new |
|
483 # instance. This is used in the admin for save_as functionality. |
|
484 AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) |
|
485 author = Author.objects.create(name='Charles Baudelaire') |
|
486 |
|
487 data = { |
|
488 'book_set-TOTAL_FORMS': '3', # the number of forms rendered |
|
489 'book_set-INITIAL_FORMS': '2', # the number of forms with initial data |
|
490 'book_set-MAX_NUM_FORMS': '', # the max number of forms |
|
491 'book_set-0-id': '1', |
|
492 'book_set-0-title': 'Les Fleurs du Mal', |
|
493 'book_set-1-id': '2', |
|
494 'book_set-1-title': 'Les Paradis Artificiels', |
|
495 'book_set-2-title': '', |
|
496 } |
|
497 |
|
498 formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True) |
|
499 self.assertTrue(formset.is_valid()) |
|
500 |
|
501 new_author = Author.objects.create(name='Charles Baudelaire') |
|
502 formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True) |
|
503 saved = formset.save() |
|
504 self.assertEqual(len(saved), 2) |
|
505 book1, book2 = saved |
|
506 self.assertEqual(book1.title, 'Les Fleurs du Mal') |
|
507 self.assertEqual(book2.title, 'Les Paradis Artificiels') |
|
508 |
|
509 # Test using a custom prefix on an inline formset. |
|
510 |
|
511 formset = AuthorBooksFormSet(prefix="test") |
|
512 self.assertEqual(len(formset.forms), 2) |
|
513 self.assertEqual(formset.forms[0].as_p(), |
|
514 '<p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0-author" id="id_test-0-author" /><input type="hidden" name="test-0-id" id="id_test-0-id" /></p>') |
|
515 self.assertEqual(formset.forms[1].as_p(), |
|
516 '<p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1-author" id="id_test-1-author" /><input type="hidden" name="test-1-id" id="id_test-1-id" /></p>') |
|
517 |
|
518 def test_inline_formsets_with_custom_pk(self): |
|
519 # Test inline formsets where the inline-edited object has a custom |
|
520 # primary key that is not the fk to the parent object. |
|
521 |
|
522 AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1) |
|
523 author = Author.objects.create(pk=1, name='Charles Baudelaire') |
|
524 |
|
525 formset = AuthorBooksFormSet2(instance=author) |
|
526 self.assertEqual(len(formset.forms), 1) |
|
527 self.assertEqual(formset.forms[0].as_p(), |
|
528 '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input type="text" name="bookwithcustompk_set-0-my_pk" id="id_bookwithcustompk_set-0-my_pk" /></p>\n' |
|
529 '<p><label for="id_bookwithcustompk_set-0-title">Title:</label> <input id="id_bookwithcustompk_set-0-title" type="text" name="bookwithcustompk_set-0-title" maxlength="100" /><input type="hidden" name="bookwithcustompk_set-0-author" value="1" id="id_bookwithcustompk_set-0-author" /></p>') |
|
530 |
|
531 data = { |
|
532 'bookwithcustompk_set-TOTAL_FORMS': '1', # the number of forms rendered |
|
533 'bookwithcustompk_set-INITIAL_FORMS': '0', # the number of forms with initial data |
|
534 'bookwithcustompk_set-MAX_NUM_FORMS': '', # the max number of forms |
|
535 'bookwithcustompk_set-0-my_pk': '77777', |
|
536 'bookwithcustompk_set-0-title': 'Les Fleurs du Mal', |
|
537 } |
|
538 |
|
539 formset = AuthorBooksFormSet2(data, instance=author) |
|
540 self.assertTrue(formset.is_valid()) |
|
541 |
|
542 saved = formset.save() |
|
543 self.assertEqual(len(saved), 1) |
|
544 book1, = saved |
|
545 self.assertEqual(book1.pk, 77777) |
|
546 |
|
547 book1 = author.bookwithcustompk_set.get() |
|
548 self.assertEqual(book1.title, 'Les Fleurs du Mal') |
|
549 |
|
550 def test_inline_formsets_with_multi_table_inheritance(self): |
|
551 # Test inline formsets where the inline-edited object uses multi-table |
|
552 # inheritance, thus has a non AutoField yet auto-created primary key. |
|
553 |
|
554 AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1) |
|
555 author = Author.objects.create(pk=1, name='Charles Baudelaire') |
|
556 |
|
557 formset = AuthorBooksFormSet3(instance=author) |
|
558 self.assertEqual(len(formset.forms), 1) |
|
559 self.assertEqual(formset.forms[0].as_p(), |
|
560 '<p><label for="id_alternatebook_set-0-title">Title:</label> <input id="id_alternatebook_set-0-title" type="text" name="alternatebook_set-0-title" maxlength="100" /></p>\n' |
|
561 '<p><label for="id_alternatebook_set-0-notes">Notes:</label> <input id="id_alternatebook_set-0-notes" type="text" name="alternatebook_set-0-notes" maxlength="100" /><input type="hidden" name="alternatebook_set-0-author" value="1" id="id_alternatebook_set-0-author" /><input type="hidden" name="alternatebook_set-0-book_ptr" id="id_alternatebook_set-0-book_ptr" /></p>') |
|
562 |
|
563 data = { |
|
564 'alternatebook_set-TOTAL_FORMS': '1', # the number of forms rendered |
|
565 'alternatebook_set-INITIAL_FORMS': '0', # the number of forms with initial data |
|
566 'alternatebook_set-MAX_NUM_FORMS': '', # the max number of forms |
|
567 'alternatebook_set-0-title': 'Flowers of Evil', |
|
568 'alternatebook_set-0-notes': 'English translation of Les Fleurs du Mal' |
|
569 } |
|
570 |
|
571 formset = AuthorBooksFormSet3(data, instance=author) |
|
572 self.assertTrue(formset.is_valid()) |
|
573 |
|
574 saved = formset.save() |
|
575 self.assertEqual(len(saved), 1) |
|
576 book1, = saved |
|
577 self.assertEqual(book1.title, 'Flowers of Evil') |
|
578 self.assertEqual(book1.notes, 'English translation of Les Fleurs du Mal') |
|
579 |
|
580 # Test inline formsets where the inline-edited object has a |
|
581 # unique_together constraint with a nullable member |
|
582 |
|
583 AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2) |
|
584 |
|
585 data = { |
|
586 'bookwithoptionalalteditor_set-TOTAL_FORMS': '2', # the number of forms rendered |
|
587 'bookwithoptionalalteditor_set-INITIAL_FORMS': '0', # the number of forms with initial data |
|
588 'bookwithoptionalalteditor_set-MAX_NUM_FORMS': '', # the max number of forms |
|
589 'bookwithoptionalalteditor_set-0-author': '1', |
|
590 'bookwithoptionalalteditor_set-0-title': 'Les Fleurs du Mal', |
|
591 'bookwithoptionalalteditor_set-1-author': '1', |
|
592 'bookwithoptionalalteditor_set-1-title': 'Les Fleurs du Mal', |
|
593 } |
|
594 formset = AuthorBooksFormSet4(data, instance=author) |
|
595 self.assertTrue(formset.is_valid()) |
|
596 |
|
597 saved = formset.save() |
|
598 self.assertEqual(len(saved), 2) |
|
599 book1, book2 = saved |
|
600 self.assertEqual(book1.author_id, 1) |
|
601 self.assertEqual(book1.title, 'Les Fleurs du Mal') |
|
602 self.assertEqual(book2.author_id, 1) |
|
603 self.assertEqual(book2.title, 'Les Fleurs du Mal') |
|
604 |
|
605 def test_inline_formsets_with_custom_save_method(self): |
|
606 AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) |
|
607 author = Author.objects.create(pk=1, name='Charles Baudelaire') |
|
608 book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels') |
|
609 book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal') |
|
610 book3 = Book.objects.create(pk=3, author=author, title='Flowers of Evil') |
|
611 |
|
612 class PoemForm(forms.ModelForm): |
|
613 def save(self, commit=True): |
|
614 # change the name to "Brooklyn Bridge" just to be a jerk. |
|
615 poem = super(PoemForm, self).save(commit=False) |
|
616 poem.name = u"Brooklyn Bridge" |
|
617 if commit: |
|
618 poem.save() |
|
619 return poem |
|
620 |
|
621 PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm) |
|
622 |
|
623 data = { |
|
624 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered |
|
625 'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data |
|
626 'poem_set-MAX_NUM_FORMS': '', # the max number of forms |
|
627 'poem_set-0-name': 'The Cloud in Trousers', |
|
628 'poem_set-1-name': 'I', |
|
629 'poem_set-2-name': '', |
|
630 } |
|
631 |
|
632 poet = Poet.objects.create(name='Vladimir Mayakovsky') |
|
633 formset = PoemFormSet(data=data, instance=poet) |
|
634 self.assertTrue(formset.is_valid()) |
|
635 |
|
636 saved = formset.save() |
|
637 self.assertEqual(len(saved), 2) |
|
638 poem1, poem2 = saved |
|
639 self.assertEqual(poem1.name, 'Brooklyn Bridge') |
|
640 self.assertEqual(poem2.name, 'Brooklyn Bridge') |
|
641 |
|
642 # We can provide a custom queryset to our InlineFormSet: |
|
643 |
|
644 custom_qs = Book.objects.order_by('-title') |
|
645 formset = AuthorBooksFormSet(instance=author, queryset=custom_qs) |
|
646 self.assertEqual(len(formset.forms), 5) |
|
647 self.assertEqual(formset.forms[0].as_p(), |
|
648 '<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Paradis Artificiels" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p>') |
|
649 self.assertEqual(formset.forms[1].as_p(), |
|
650 '<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" value="2" id="id_book_set-1-id" /></p>') |
|
651 self.assertEqual(formset.forms[2].as_p(), |
|
652 '<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" value="3" id="id_book_set-2-id" /></p>') |
|
653 self.assertEqual(formset.forms[3].as_p(), |
|
654 '<p><label for="id_book_set-3-title">Title:</label> <input id="id_book_set-3-title" type="text" name="book_set-3-title" maxlength="100" /><input type="hidden" name="book_set-3-author" value="1" id="id_book_set-3-author" /><input type="hidden" name="book_set-3-id" id="id_book_set-3-id" /></p>') |
|
655 self.assertEqual(formset.forms[4].as_p(), |
|
656 '<p><label for="id_book_set-4-title">Title:</label> <input id="id_book_set-4-title" type="text" name="book_set-4-title" maxlength="100" /><input type="hidden" name="book_set-4-author" value="1" id="id_book_set-4-author" /><input type="hidden" name="book_set-4-id" id="id_book_set-4-id" /></p>') |
|
657 |
|
658 data = { |
|
659 'book_set-TOTAL_FORMS': '5', # the number of forms rendered |
|
660 'book_set-INITIAL_FORMS': '3', # the number of forms with initial data |
|
661 'book_set-MAX_NUM_FORMS': '', # the max number of forms |
|
662 'book_set-0-id': str(book1.id), |
|
663 'book_set-0-title': 'Les Paradis Artificiels', |
|
664 'book_set-1-id': str(book2.id), |
|
665 'book_set-1-title': 'Les Fleurs du Mal', |
|
666 'book_set-2-id': str(book3.id), |
|
667 'book_set-2-title': 'Flowers of Evil', |
|
668 'book_set-3-title': 'Revue des deux mondes', |
|
669 'book_set-4-title': '', |
|
670 } |
|
671 formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs) |
|
672 self.assertTrue(formset.is_valid()) |
|
673 |
|
674 custom_qs = Book.objects.filter(title__startswith='F') |
|
675 formset = AuthorBooksFormSet(instance=author, queryset=custom_qs) |
|
676 self.assertEqual(formset.forms[0].as_p(), |
|
677 '<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="3" id="id_book_set-0-id" /></p>') |
|
678 self.assertEqual(formset.forms[1].as_p(), |
|
679 '<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>') |
|
680 self.assertEqual(formset.forms[2].as_p(), |
|
681 '<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>') |
|
682 |
|
683 data = { |
|
684 'book_set-TOTAL_FORMS': '3', # the number of forms rendered |
|
685 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data |
|
686 'book_set-MAX_NUM_FORMS': '', # the max number of forms |
|
687 'book_set-0-id': str(book3.id), |
|
688 'book_set-0-title': 'Flowers of Evil', |
|
689 'book_set-1-title': 'Revue des deux mondes', |
|
690 'book_set-2-title': '', |
|
691 } |
|
692 formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs) |
|
693 self.assertTrue(formset.is_valid()) |
|
694 |
|
695 def test_custom_pk(self): |
|
696 # We need to ensure that it is displayed |
|
697 |
|
698 CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey) |
|
699 formset = CustomPrimaryKeyFormSet() |
|
700 self.assertEqual(len(formset.forms), 1) |
|
701 self.assertEqual(formset.forms[0].as_p(), |
|
702 '<p><label for="id_form-0-my_pk">My pk:</label> <input id="id_form-0-my_pk" type="text" name="form-0-my_pk" maxlength="10" /></p>\n' |
|
703 '<p><label for="id_form-0-some_field">Some field:</label> <input id="id_form-0-some_field" type="text" name="form-0-some_field" maxlength="100" /></p>') |
|
704 |
|
705 # Custom primary keys with ForeignKey, OneToOneField and AutoField ############ |
|
706 |
|
707 place = Place.objects.create(pk=1, name=u'Giordanos', city=u'Chicago') |
|
708 |
|
709 FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False) |
|
710 formset = FormSet(instance=place) |
|
711 self.assertEqual(len(formset.forms), 2) |
|
712 self.assertEqual(formset.forms[0].as_p(), |
|
713 '<p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" maxlength="100" /><input type="hidden" name="owner_set-0-place" value="1" id="id_owner_set-0-place" /><input type="hidden" name="owner_set-0-auto_id" id="id_owner_set-0-auto_id" /></p>') |
|
714 self.assertEqual(formset.forms[1].as_p(), |
|
715 '<p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1-place" value="1" id="id_owner_set-1-place" /><input type="hidden" name="owner_set-1-auto_id" id="id_owner_set-1-auto_id" /></p>') |
|
716 |
|
717 data = { |
|
718 'owner_set-TOTAL_FORMS': '2', |
|
719 'owner_set-INITIAL_FORMS': '0', |
|
720 'owner_set-MAX_NUM_FORMS': '', |
|
721 'owner_set-0-auto_id': '', |
|
722 'owner_set-0-name': u'Joe Perry', |
|
723 'owner_set-1-auto_id': '', |
|
724 'owner_set-1-name': '', |
|
725 } |
|
726 formset = FormSet(data, instance=place) |
|
727 self.assertTrue(formset.is_valid()) |
|
728 saved = formset.save() |
|
729 self.assertEqual(len(saved), 1) |
|
730 owner, = saved |
|
731 self.assertEqual(owner.name, 'Joe Perry') |
|
732 self.assertEqual(owner.place.name, 'Giordanos') |
|
733 |
|
734 formset = FormSet(instance=place) |
|
735 self.assertEqual(len(formset.forms), 3) |
|
736 self.assertEqual(formset.forms[0].as_p(), |
|
737 '<p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" value="Joe Perry" maxlength="100" /><input type="hidden" name="owner_set-0-place" value="1" id="id_owner_set-0-place" /><input type="hidden" name="owner_set-0-auto_id" value="1" id="id_owner_set-0-auto_id" /></p>') |
|
738 self.assertEqual(formset.forms[1].as_p(), |
|
739 '<p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1-place" value="1" id="id_owner_set-1-place" /><input type="hidden" name="owner_set-1-auto_id" id="id_owner_set-1-auto_id" /></p>') |
|
740 self.assertEqual(formset.forms[2].as_p(), |
|
741 '<p><label for="id_owner_set-2-name">Name:</label> <input id="id_owner_set-2-name" type="text" name="owner_set-2-name" maxlength="100" /><input type="hidden" name="owner_set-2-place" value="1" id="id_owner_set-2-place" /><input type="hidden" name="owner_set-2-auto_id" id="id_owner_set-2-auto_id" /></p>') |
|
742 |
|
743 data = { |
|
744 'owner_set-TOTAL_FORMS': '3', |
|
745 'owner_set-INITIAL_FORMS': '1', |
|
746 'owner_set-MAX_NUM_FORMS': '', |
|
747 'owner_set-0-auto_id': u'1', |
|
748 'owner_set-0-name': u'Joe Perry', |
|
749 'owner_set-1-auto_id': '', |
|
750 'owner_set-1-name': u'Jack Berry', |
|
751 'owner_set-2-auto_id': '', |
|
752 'owner_set-2-name': '', |
|
753 } |
|
754 formset = FormSet(data, instance=place) |
|
755 self.assertTrue(formset.is_valid()) |
|
756 saved = formset.save() |
|
757 self.assertEqual(len(saved), 1) |
|
758 owner, = saved |
|
759 self.assertEqual(owner.name, 'Jack Berry') |
|
760 self.assertEqual(owner.place.name, 'Giordanos') |
|
761 |
|
762 # Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose. |
|
763 |
|
764 FormSet = modelformset_factory(OwnerProfile) |
|
765 formset = FormSet() |
|
766 self.assertEqual(formset.forms[0].as_p(), |
|
767 '<p><label for="id_form-0-owner">Owner:</label> <select name="form-0-owner" id="id_form-0-owner">\n' |
|
768 '<option value="" selected="selected">---------</option>\n' |
|
769 '<option value="1">Joe Perry at Giordanos</option>\n' |
|
770 '<option value="2">Jack Berry at Giordanos</option>\n' |
|
771 '</select></p>\n' |
|
772 '<p><label for="id_form-0-age">Age:</label> <input type="text" name="form-0-age" id="id_form-0-age" /></p>') |
|
773 |
|
774 owner = Owner.objects.get(name=u'Joe Perry') |
|
775 FormSet = inlineformset_factory(Owner, OwnerProfile, max_num=1, can_delete=False) |
|
776 self.assertEqual(FormSet.max_num, 1) |
|
777 |
|
778 formset = FormSet(instance=owner) |
|
779 self.assertEqual(len(formset.forms), 1) |
|
780 self.assertEqual(formset.forms[0].as_p(), |
|
781 '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="1" id="id_ownerprofile-0-owner" /></p>') |
|
782 |
|
783 data = { |
|
784 'ownerprofile-TOTAL_FORMS': '1', |
|
785 'ownerprofile-INITIAL_FORMS': '0', |
|
786 'ownerprofile-MAX_NUM_FORMS': '1', |
|
787 'ownerprofile-0-owner': '', |
|
788 'ownerprofile-0-age': u'54', |
|
789 } |
|
790 formset = FormSet(data, instance=owner) |
|
791 self.assertTrue(formset.is_valid()) |
|
792 saved = formset.save() |
|
793 self.assertEqual(len(saved), 1) |
|
794 profile1, = saved |
|
795 self.assertEqual(profile1.owner, owner) |
|
796 self.assertEqual(profile1.age, 54) |
|
797 |
|
798 formset = FormSet(instance=owner) |
|
799 self.assertEqual(len(formset.forms), 1) |
|
800 self.assertEqual(formset.forms[0].as_p(), |
|
801 '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" value="54" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="1" id="id_ownerprofile-0-owner" /></p>') |
|
802 |
|
803 data = { |
|
804 'ownerprofile-TOTAL_FORMS': '1', |
|
805 'ownerprofile-INITIAL_FORMS': '1', |
|
806 'ownerprofile-MAX_NUM_FORMS': '1', |
|
807 'ownerprofile-0-owner': u'1', |
|
808 'ownerprofile-0-age': u'55', |
|
809 } |
|
810 formset = FormSet(data, instance=owner) |
|
811 self.assertTrue(formset.is_valid()) |
|
812 saved = formset.save() |
|
813 self.assertEqual(len(saved), 1) |
|
814 profile1, = saved |
|
815 self.assertEqual(profile1.owner, owner) |
|
816 self.assertEqual(profile1.age, 55) |
|
817 |
|
818 def test_unique_true_enforces_max_num_one(self): |
|
819 # ForeignKey with unique=True should enforce max_num=1 |
|
820 |
|
821 place = Place.objects.create(pk=1, name=u'Giordanos', city=u'Chicago') |
|
822 |
|
823 FormSet = inlineformset_factory(Place, Location, can_delete=False) |
|
824 self.assertEqual(FormSet.max_num, 1) |
|
825 |
|
826 formset = FormSet(instance=place) |
|
827 self.assertEqual(len(formset.forms), 1) |
|
828 self.assertEqual(formset.forms[0].as_p(), |
|
829 '<p><label for="id_location_set-0-lat">Lat:</label> <input id="id_location_set-0-lat" type="text" name="location_set-0-lat" maxlength="100" /></p>\n' |
|
830 '<p><label for="id_location_set-0-lon">Lon:</label> <input id="id_location_set-0-lon" type="text" name="location_set-0-lon" maxlength="100" /><input type="hidden" name="location_set-0-place" value="1" id="id_location_set-0-place" /><input type="hidden" name="location_set-0-id" id="id_location_set-0-id" /></p>') |
|
831 |
|
832 def test_foreign_keys_in_parents(self): |
|
833 self.assertEqual(type(_get_foreign_key(Restaurant, Owner)), models.ForeignKey) |
|
834 self.assertEqual(type(_get_foreign_key(MexicanRestaurant, Owner)), models.ForeignKey) |
|
835 |
|
836 def test_unique_validation(self): |
|
837 FormSet = modelformset_factory(Product, extra=1) |
|
838 data = { |
|
839 'form-TOTAL_FORMS': '1', |
|
840 'form-INITIAL_FORMS': '0', |
|
841 'form-MAX_NUM_FORMS': '', |
|
842 'form-0-slug': 'car-red', |
|
843 } |
|
844 formset = FormSet(data) |
|
845 self.assertTrue(formset.is_valid()) |
|
846 saved = formset.save() |
|
847 self.assertEqual(len(saved), 1) |
|
848 product1, = saved |
|
849 self.assertEqual(product1.slug, 'car-red') |
|
850 |
|
851 data = { |
|
852 'form-TOTAL_FORMS': '1', |
|
853 'form-INITIAL_FORMS': '0', |
|
854 'form-MAX_NUM_FORMS': '', |
|
855 'form-0-slug': 'car-red', |
|
856 } |
|
857 formset = FormSet(data) |
|
858 self.assertFalse(formset.is_valid()) |
|
859 self.assertEqual(formset.errors, [{'slug': [u'Product with this Slug already exists.']}]) |
|
860 |
|
861 def test_unique_together_validation(self): |
|
862 FormSet = modelformset_factory(Price, extra=1) |
|
863 data = { |
|
864 'form-TOTAL_FORMS': '1', |
|
865 'form-INITIAL_FORMS': '0', |
|
866 'form-MAX_NUM_FORMS': '', |
|
867 'form-0-price': u'12.00', |
|
868 'form-0-quantity': '1', |
|
869 } |
|
870 formset = FormSet(data) |
|
871 self.assertTrue(formset.is_valid()) |
|
872 saved = formset.save() |
|
873 self.assertEqual(len(saved), 1) |
|
874 price1, = saved |
|
875 self.assertEqual(price1.price, Decimal('12.00')) |
|
876 self.assertEqual(price1.quantity, 1) |
|
877 |
|
878 data = { |
|
879 'form-TOTAL_FORMS': '1', |
|
880 'form-INITIAL_FORMS': '0', |
|
881 'form-MAX_NUM_FORMS': '', |
|
882 'form-0-price': u'12.00', |
|
883 'form-0-quantity': '1', |
|
884 } |
|
885 formset = FormSet(data) |
|
886 self.assertFalse(formset.is_valid()) |
|
887 self.assertEqual(formset.errors, [{'__all__': [u'Price with this Price and Quantity already exists.']}]) |
|
888 |
|
889 def test_unique_together_with_inlineformset_factory(self): |
|
890 # Also see bug #8882. |
|
891 |
|
892 repository = Repository.objects.create(name=u'Test Repo') |
|
893 FormSet = inlineformset_factory(Repository, Revision, extra=1) |
|
894 data = { |
|
895 'revision_set-TOTAL_FORMS': '1', |
|
896 'revision_set-INITIAL_FORMS': '0', |
|
897 'revision_set-MAX_NUM_FORMS': '', |
|
898 'revision_set-0-repository': repository.pk, |
|
899 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', |
|
900 'revision_set-0-DELETE': '', |
|
901 } |
|
902 formset = FormSet(data, instance=repository) |
|
903 self.assertTrue(formset.is_valid()) |
|
904 saved = formset.save() |
|
905 self.assertEqual(len(saved), 1) |
|
906 revision1, = saved |
|
907 self.assertEqual(revision1.repository, repository) |
|
908 self.assertEqual(revision1.revision, '146239817507f148d448db38840db7c3cbf47c76') |
|
909 |
|
910 # attempt to save the same revision against against the same repo. |
|
911 data = { |
|
912 'revision_set-TOTAL_FORMS': '1', |
|
913 'revision_set-INITIAL_FORMS': '0', |
|
914 'revision_set-MAX_NUM_FORMS': '', |
|
915 'revision_set-0-repository': repository.pk, |
|
916 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', |
|
917 'revision_set-0-DELETE': '', |
|
918 } |
|
919 formset = FormSet(data, instance=repository) |
|
920 self.assertFalse(formset.is_valid()) |
|
921 self.assertEqual(formset.errors, [{'__all__': [u'Revision with this Repository and Revision already exists.']}]) |
|
922 |
|
923 # unique_together with inlineformset_factory with overridden form fields |
|
924 # Also see #9494 |
|
925 |
|
926 FormSet = inlineformset_factory(Repository, Revision, fields=('revision',), extra=1) |
|
927 data = { |
|
928 'revision_set-TOTAL_FORMS': '1', |
|
929 'revision_set-INITIAL_FORMS': '0', |
|
930 'revision_set-MAX_NUM_FORMS': '', |
|
931 'revision_set-0-repository': repository.pk, |
|
932 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', |
|
933 'revision_set-0-DELETE': '', |
|
934 } |
|
935 formset = FormSet(data, instance=repository) |
|
936 self.assertFalse(formset.is_valid()) |
|
937 |
|
938 def test_callable_defaults(self): |
|
939 # Use of callable defaults (see bug #7975). |
|
940 |
|
941 person = Person.objects.create(name='Ringo') |
|
942 FormSet = inlineformset_factory(Person, Membership, can_delete=False, extra=1) |
|
943 formset = FormSet(instance=person) |
|
944 |
|
945 # Django will render a hidden field for model fields that have a callable |
|
946 # default. This is required to ensure the value is tested for change correctly |
|
947 # when determine what extra forms have changed to save. |
|
948 |
|
949 self.assertEquals(len(formset.forms), 1) # this formset only has one form |
|
950 form = formset.forms[0] |
|
951 now = form.fields['date_joined'].initial() |
|
952 result = form.as_p() |
|
953 result = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?', '__DATETIME__', result) |
|
954 self.assertEqual(result, |
|
955 '<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="__DATETIME__" id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="__DATETIME__" id="initial-membership_set-0-id_membership_set-0-date_joined" /></p>\n' |
|
956 '<p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="1" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p>') |
|
957 |
|
958 # test for validation with callable defaults. Validations rely on hidden fields |
|
959 |
|
960 data = { |
|
961 'membership_set-TOTAL_FORMS': '1', |
|
962 'membership_set-INITIAL_FORMS': '0', |
|
963 'membership_set-MAX_NUM_FORMS': '', |
|
964 'membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), |
|
965 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), |
|
966 'membership_set-0-karma': '', |
|
967 } |
|
968 formset = FormSet(data, instance=person) |
|
969 self.assertTrue(formset.is_valid()) |
|
970 |
|
971 # now test for when the data changes |
|
972 |
|
973 one_day_later = now + datetime.timedelta(days=1) |
|
974 filled_data = { |
|
975 'membership_set-TOTAL_FORMS': '1', |
|
976 'membership_set-INITIAL_FORMS': '0', |
|
977 'membership_set-MAX_NUM_FORMS': '', |
|
978 'membership_set-0-date_joined': unicode(one_day_later.strftime('%Y-%m-%d %H:%M:%S')), |
|
979 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), |
|
980 'membership_set-0-karma': '', |
|
981 } |
|
982 formset = FormSet(filled_data, instance=person) |
|
983 self.assertFalse(formset.is_valid()) |
|
984 |
|
985 # now test with split datetime fields |
|
986 |
|
987 class MembershipForm(forms.ModelForm): |
|
988 date_joined = forms.SplitDateTimeField(initial=now) |
|
989 class Meta: |
|
990 model = Membership |
|
991 def __init__(self, **kwargs): |
|
992 super(MembershipForm, self).__init__(**kwargs) |
|
993 self.fields['date_joined'].widget = forms.SplitDateTimeWidget() |
|
994 |
|
995 FormSet = inlineformset_factory(Person, Membership, form=MembershipForm, can_delete=False, extra=1) |
|
996 data = { |
|
997 'membership_set-TOTAL_FORMS': '1', |
|
998 'membership_set-INITIAL_FORMS': '0', |
|
999 'membership_set-MAX_NUM_FORMS': '', |
|
1000 'membership_set-0-date_joined_0': unicode(now.strftime('%Y-%m-%d')), |
|
1001 'membership_set-0-date_joined_1': unicode(now.strftime('%H:%M:%S')), |
|
1002 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), |
|
1003 'membership_set-0-karma': '', |
|
1004 } |
|
1005 formset = FormSet(data, instance=person) |
|
1006 self.assertTrue(formset.is_valid()) |
|
1007 |
|
1008 def test_inlineformset_factory_with_null_fk(self): |
|
1009 # inlineformset_factory tests with fk having null=True. see #9462. |
|
1010 # create some data that will exbit the issue |
|
1011 team = Team.objects.create(name=u"Red Vipers") |
|
1012 Player(name="Timmy").save() |
|
1013 Player(name="Bobby", team=team).save() |
|
1014 |
|
1015 PlayerInlineFormSet = inlineformset_factory(Team, Player) |
|
1016 formset = PlayerInlineFormSet() |
|
1017 self.assertQuerysetEqual(formset.get_queryset(), []) |
|
1018 |
|
1019 formset = PlayerInlineFormSet(instance=team) |
|
1020 players = formset.get_queryset() |
|
1021 self.assertEqual(len(players), 1) |
|
1022 player1, = players |
|
1023 self.assertEqual(player1.team, team) |
|
1024 self.assertEqual(player1.name, 'Bobby') |
|
1025 |
|
1026 def test_model_formset_with_custom_pk(self): |
|
1027 # a formset for a Model that has a custom primary key that still needs to be |
|
1028 # added to the formset automatically |
|
1029 FormSet = modelformset_factory(ClassyMexicanRestaurant, fields=["tacos_are_yummy"]) |
|
1030 self.assertEqual(sorted(FormSet().forms[0].fields.keys()), ['restaurant', 'tacos_are_yummy']) |
|
1031 |
|
1032 def test_prevent_duplicates_from_with_the_same_formset(self): |
|
1033 FormSet = modelformset_factory(Product, extra=2) |
|
1034 data = { |
|
1035 'form-TOTAL_FORMS': 2, |
|
1036 'form-INITIAL_FORMS': 0, |
|
1037 'form-MAX_NUM_FORMS': '', |
|
1038 'form-0-slug': 'red_car', |
|
1039 'form-1-slug': 'red_car', |
|
1040 } |
|
1041 formset = FormSet(data) |
|
1042 self.assertFalse(formset.is_valid()) |
|
1043 self.assertEqual(formset._non_form_errors, |
|
1044 [u'Please correct the duplicate data for slug.']) |
|
1045 |
|
1046 FormSet = modelformset_factory(Price, extra=2) |
|
1047 data = { |
|
1048 'form-TOTAL_FORMS': 2, |
|
1049 'form-INITIAL_FORMS': 0, |
|
1050 'form-MAX_NUM_FORMS': '', |
|
1051 'form-0-price': '25', |
|
1052 'form-0-quantity': '7', |
|
1053 'form-1-price': '25', |
|
1054 'form-1-quantity': '7', |
|
1055 } |
|
1056 formset = FormSet(data) |
|
1057 self.assertFalse(formset.is_valid()) |
|
1058 self.assertEqual(formset._non_form_errors, |
|
1059 [u'Please correct the duplicate data for price and quantity, which must be unique.']) |
|
1060 |
|
1061 # Only the price field is specified, this should skip any unique checks since |
|
1062 # the unique_together is not fulfilled. This will fail with a KeyError if broken. |
|
1063 FormSet = modelformset_factory(Price, fields=("price",), extra=2) |
|
1064 data = { |
|
1065 'form-TOTAL_FORMS': '2', |
|
1066 'form-INITIAL_FORMS': '0', |
|
1067 'form-MAX_NUM_FORMS': '', |
|
1068 'form-0-price': '24', |
|
1069 'form-1-price': '24', |
|
1070 } |
|
1071 formset = FormSet(data) |
|
1072 self.assertTrue(formset.is_valid()) |
|
1073 |
|
1074 FormSet = inlineformset_factory(Author, Book, extra=0) |
|
1075 author = Author.objects.create(pk=1, name='Charles Baudelaire') |
|
1076 book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels') |
|
1077 book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal') |
|
1078 book3 = Book.objects.create(pk=3, author=author, title='Flowers of Evil') |
|
1079 |
|
1080 book_ids = author.book_set.order_by('id').values_list('id', flat=True) |
|
1081 data = { |
|
1082 'book_set-TOTAL_FORMS': '2', |
|
1083 'book_set-INITIAL_FORMS': '2', |
|
1084 'book_set-MAX_NUM_FORMS': '', |
|
1085 |
|
1086 'book_set-0-title': 'The 2008 Election', |
|
1087 'book_set-0-author': str(author.id), |
|
1088 'book_set-0-id': str(book_ids[0]), |
|
1089 |
|
1090 'book_set-1-title': 'The 2008 Election', |
|
1091 'book_set-1-author': str(author.id), |
|
1092 'book_set-1-id': str(book_ids[1]), |
|
1093 } |
|
1094 formset = FormSet(data=data, instance=author) |
|
1095 self.assertFalse(formset.is_valid()) |
|
1096 self.assertEqual(formset._non_form_errors, |
|
1097 [u'Please correct the duplicate data for title.']) |
|
1098 self.assertEqual(formset.errors, |
|
1099 [{}, {'__all__': u'Please correct the duplicate values below.'}]) |
|
1100 |
|
1101 FormSet = modelformset_factory(Post, extra=2) |
|
1102 data = { |
|
1103 'form-TOTAL_FORMS': '2', |
|
1104 'form-INITIAL_FORMS': '0', |
|
1105 'form-MAX_NUM_FORMS': '', |
|
1106 |
|
1107 'form-0-title': 'blah', |
|
1108 'form-0-slug': 'Morning', |
|
1109 'form-0-subtitle': 'foo', |
|
1110 'form-0-posted': '2009-01-01', |
|
1111 'form-1-title': 'blah', |
|
1112 'form-1-slug': 'Morning in Prague', |
|
1113 'form-1-subtitle': 'rawr', |
|
1114 'form-1-posted': '2009-01-01' |
|
1115 } |
|
1116 formset = FormSet(data) |
|
1117 self.assertFalse(formset.is_valid()) |
|
1118 self.assertEqual(formset._non_form_errors, |
|
1119 [u'Please correct the duplicate data for title which must be unique for the date in posted.']) |
|
1120 self.assertEqual(formset.errors, |
|
1121 [{}, {'__all__': u'Please correct the duplicate values below.'}]) |
|
1122 |
|
1123 data = { |
|
1124 'form-TOTAL_FORMS': '2', |
|
1125 'form-INITIAL_FORMS': '0', |
|
1126 'form-MAX_NUM_FORMS': '', |
|
1127 |
|
1128 'form-0-title': 'foo', |
|
1129 'form-0-slug': 'Morning in Prague', |
|
1130 'form-0-subtitle': 'foo', |
|
1131 'form-0-posted': '2009-01-01', |
|
1132 'form-1-title': 'blah', |
|
1133 'form-1-slug': 'Morning in Prague', |
|
1134 'form-1-subtitle': 'rawr', |
|
1135 'form-1-posted': '2009-08-02' |
|
1136 } |
|
1137 formset = FormSet(data) |
|
1138 self.assertFalse(formset.is_valid()) |
|
1139 self.assertEqual(formset._non_form_errors, |
|
1140 [u'Please correct the duplicate data for slug which must be unique for the year in posted.']) |
|
1141 |
|
1142 data = { |
|
1143 'form-TOTAL_FORMS': '2', |
|
1144 'form-INITIAL_FORMS': '0', |
|
1145 'form-MAX_NUM_FORMS': '', |
|
1146 |
|
1147 'form-0-title': 'foo', |
|
1148 'form-0-slug': 'Morning in Prague', |
|
1149 'form-0-subtitle': 'rawr', |
|
1150 'form-0-posted': '2008-08-01', |
|
1151 'form-1-title': 'blah', |
|
1152 'form-1-slug': 'Prague', |
|
1153 'form-1-subtitle': 'rawr', |
|
1154 'form-1-posted': '2009-08-02' |
|
1155 } |
|
1156 formset = FormSet(data) |
|
1157 self.assertFalse(formset.is_valid()) |
|
1158 self.assertEqual(formset._non_form_errors, |
|
1159 [u'Please correct the duplicate data for subtitle which must be unique for the month in posted.']) |