PageRenderTime 136ms CodeModel.GetById 44ms app.highlight 78ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/modeltests/model_formsets/tests.py

https://code.google.com/p/mango-py/
Python | 1167 lines | 1130 code | 20 blank | 17 comment | 1 complexity | 55bf10a4f8e5feb4ddb4bcba734f5976 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1import datetime
  2import re
  3from datetime import date
  4from decimal import Decimal
  5
  6from django import forms
  7from django.db import models
  8from django.forms.models import (_get_foreign_key, inlineformset_factory,
  9    modelformset_factory, modelformset_factory)
 10from django.test import TestCase, skipUnlessDBFeature
 11
 12from 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
 19class 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': unicode(poet.id),
 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
 89class 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': str(meeting.id),
254            'form-0-name': '2nd Tuesday of the Week Meeting',
255            'form-0-authors': [author2.id, author1.id, author3.id, author4.id],
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': str(book1.id),
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    @skipUnlessDBFeature('ignores_nulls_in_unique_constraints')
581    def test_inline_formsets_with_nullable_unique_together(self):
582        # Test inline formsets where the inline-edited object has a
583        # unique_together constraint with a nullable member
584
585        AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2)
586        author = Author.objects.create(pk=1, name='Charles Baudelaire')
587
588        data = {
589            'bookwithoptionalalteditor_set-TOTAL_FORMS': '2', # the number of forms rendered
590            'bookwithoptionalalteditor_set-INITIAL_FORMS': '0', # the number of forms with initial data
591            'bookwithoptionalalteditor_set-MAX_NUM_FORMS': '', # the max number of forms
592            'bookwithoptionalalteditor_set-0-author': '1',
593            'bookwithoptionalalteditor_set-0-title': 'Les Fleurs du Mal',
594            'bookwithoptionalalteditor_set-1-author': '1',
595            'bookwithoptionalalteditor_set-1-title': 'Les Fleurs du Mal',
596        }
597        formset = AuthorBooksFormSet4(data, instance=author)
598        self.assertTrue(formset.is_valid())
599
600        saved = formset.save()
601        self.assertEqual(len(saved), 2)
602        book1, book2 = saved
603        self.assertEqual(book1.author_id, 1)
604        self.assertEqual(book1.title, 'Les Fleurs du Mal')
605        self.assertEqual(book2.author_id, 1)
606        self.assertEqual(book2.title, 'Les Fleurs du Mal')
607
608    def test_inline_formsets_with_custom_save_method(self):
609        AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2)
610        author = Author.objects.create(pk=1, name='Charles Baudelaire')
611        book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels')
612        book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal')
613        book3 = Book.objects.create(pk=3, author=author, title='Flowers of Evil')
614
615        class PoemForm(forms.ModelForm):
616            def save(self, commit=True):
617                # change the name to "Brooklyn Bridge" just to be a jerk.
618                poem = super(PoemForm, self).save(commit=False)
619                poem.name = u"Brooklyn Bridge"
620                if commit:
621                    poem.save()
622                return poem
623
624        PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm)
625
626        data = {
627            'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
628            'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data
629            'poem_set-MAX_NUM_FORMS': '', # the max number of forms
630            'poem_set-0-name': 'The Cloud in Trousers',
631            'poem_set-1-name': 'I',
632            'poem_set-2-name': '',
633        }
634
635        poet = Poet.objects.create(name='Vladimir Mayakovsky')
636        formset = PoemFormSet(data=data, instance=poet)
637        self.assertTrue(formset.is_valid())
638
639        saved = formset.save()
640        self.assertEqual(len(saved), 2)
641        poem1, poem2 = saved
642        self.assertEqual(poem1.name, 'Brooklyn Bridge')
643        self.assertEqual(poem2.name, 'Brooklyn Bridge')
644
645        # We can provide a custom queryset to our InlineFormSet:
646
647        custom_qs = Book.objects.order_by('-title')
648        formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
649        self.assertEqual(len(formset.forms), 5)
650        self.assertEqual(formset.forms[0].as_p(),
651            '<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>')
652        self.assertEqual(formset.forms[1].as_p(),
653            '<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>')
654        self.assertEqual(formset.forms[2].as_p(),
655            '<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>')
656        self.assertEqual(formset.forms[3].as_p(),
657            '<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>')
658        self.assertEqual(formset.forms[4].as_p(),
659            '<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>')
660
661        data = {
662            'book_set-TOTAL_FORMS': '5', # the number of forms rendered
663            'book_set-INITIAL_FORMS': '3', # the number of forms with initial data
664            'book_set-MAX_NUM_FORMS': '', # the max number of forms
665            'book_set-0-id': str(book1.id),
666            'book_set-0-title': 'Les Paradis Artificiels',
667            'book_set-1-id': str(book2.id),
668            'book_set-1-title': 'Les Fleurs du Mal',
669            'book_set-2-id': str(book3.id),
670            'book_set-2-title': 'Flowers of Evil',
671            'book_set-3-title': 'Revue des deux mondes',
672            'book_set-4-title': '',
673        }
674        formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
675        self.assertTrue(formset.is_valid())
676
677        custom_qs = Book.objects.filter(title__startswith='F')
678        formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
679        self.assertEqual(formset.forms[0].as_p(),
680            '<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>')
681        self.assertEqual(formset.forms[1].as_p(),
682            '<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>')
683        self.assertEqual(formset.forms[2].as_p(),
684            '<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>')
685
686        data = {
687            'book_set-TOTAL_FORMS': '3', # the number of forms rendered
688            'book_set-INITIAL_FORMS': '1', # the number of forms with initial data
689            'book_set-MAX_NUM_FORMS': '', # the max number of forms
690            'book_set-0-id': str(book3.id),
691            'book_set-0-title': 'Flowers of Evil',
692            'book_set-1-title': 'Revue des deux mondes',
693            'book_set-2-title': '',
694        }
695        formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
696        self.assertTrue(formset.is_valid())
697
698    def test_custom_pk(self):
699        # We need to ensure that it is displayed
700
701        CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey)
702        formset = CustomPrimaryKeyFormSet()
703        self.assertEqual(len(formset.forms), 1)
704        self.assertEqual(formset.forms[0].as_p(),
705            '<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'
706            '<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>')
707
708        # Custom primary keys with ForeignKey, OneToOneField and AutoField ############
709
710        place = Place.objects.create(pk=1, name=u'Giordanos', city=u'Chicago')
711
712        FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False)
713        formset = FormSet(instance=place)
714        self.assertEqual(len(formset.forms), 2)
715        self.assertEqual(formset.forms[0].as_p(),
716            '<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>')
717        self.assertEqual(formset.forms[1].as_p(),
718            '<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>')
719
720        data = {
721            'owner_set-TOTAL_FORMS': '2',
722            'owner_set-INITIAL_FORMS': '0',
723            'owner_set-MAX_NUM_FORMS': '',
724            'owner_set-0-auto_id': '',
725            'owner_set-0-name': u'Joe Perry',
726            'owner_set-1-auto_id': '',
727            'owner_set-1-name': '',
728        }
729        formset = FormSet(data, instance=place)
730        self.assertTrue(formset.is_valid())
731        saved = formset.save()
732        self.assertEqual(len(saved), 1)
733        owner1, = saved
734        self.assertEqual(owner1.name, 'Joe Perry')
735        self.assertEqual(owner1.place.name, 'Giordanos')
736
737        formset = FormSet(instance=place)
738        self.assertEqual(len(formset.forms), 3)
739        self.assertEqual(formset.forms[0].as_p(),
740            '<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="%d" id="id_owner_set-0-auto_id" /></p>'
741            % owner1.auto_id)
742        self.assertEqual(formset.forms[1].as_p(),
743            '<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>')
744        self.assertEqual(formset.forms[2].as_p(),
745            '<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>')
746
747        data = {
748            'owner_set-TOTAL_FORMS': '3',
749            'owner_set-INITIAL_FORMS': '1',
750            'owner_set-MAX_NUM_FORMS': '',
751            'owner_set-0-auto_id': unicode(owner1.auto_id),
752            'owner_set-0-name': u'Joe Perry',
753            'owner_set-1-auto_id': '',
754            'owner_set-1-name': u'Jack Berry',
755            'owner_set-2-auto_id': '',
756            'owner_set-2-name': '',
757        }
758        formset = FormSet(data, instance=place)
759        self.assertTrue(formset.is_valid())
760        saved = formset.save()
761        self.assertEqual(len(saved), 1)
762        owner2, = saved
763        self.assertEqual(owner2.name, 'Jack Berry')
764        self.assertEqual(owner2.place.name, 'Giordanos')
765
766        # Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose.
767
768        FormSet = modelformset_factory(OwnerProfile)
769        formset = FormSet()
770        self.assertEqual(formset.forms[0].as_p(),
771            '<p><label for="id_form-0-owner">Owner:</label> <select name="form-0-owner" id="id_form-0-owner">\n'
772            '<option value="" selected="selected">---------</option>\n'
773            '<option value="%d">Joe Perry at Giordanos</option>\n'
774            '<option value="%d">Jack Berry at Giordanos</option>\n'
775            '</select></p>\n'
776            '<p><label for="id_form-0-age">Age:</label> <input type="text" name="form-0-age" id="id_form-0-age" /></p>'
777            % (owner1.auto_id, owner2.auto_id))
778
779        owner1 = Owner.objects.get(name=u'Joe Perry')
780        FormSet = inlineformset_factory(Owner, OwnerProfile, max_num=1, can_delete=False)
781        self.assertEqual(FormSet.max_num, 1)
782
783        formset = FormSet(instance=owner1)
784        self.assertEqual(len(formset.forms), 1)
785        self.assertEqual(formset.forms[0].as_p(),
786            '<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="%d" id="id_ownerprofile-0-owner" /></p>'
787            % owner1.auto_id)
788
789        data = {
790            'ownerprofile-TOTAL_FORMS': '1',
791            'ownerprofile-INITIAL_FORMS': '0',
792            'ownerprofile-MAX_NUM_FORMS': '1',
793            'ownerprofile-0-owner': '',
794            'ownerprofile-0-age': u'54',
795        }
796        formset = FormSet(data, instance=owner1)
797        self.assertTrue(formset.is_valid())
798        saved = formset.save()
799        self.assertEqual(len(saved), 1)
800        profile1, = saved
801        self.assertEqual(profile1.owner, owner1)
802        self.assertEqual(profile1.age, 54)
803
804        formset = FormSet(instance=owner1)
805        self.assertEqual(len(formset.forms), 1)
806        self.assertEqual(formset.forms[0].as_p(),
807            '<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="%d" id="id_ownerprofile-0-owner" /></p>'
808            % owner1.auto_id)
809
810        data = {
811            'ownerprofile-TOTAL_FORMS': '1',
812            'ownerprofile-INITIAL_FORMS': '1',
813            'ownerprofile-MAX_NUM_FORMS': '1',
814            'ownerprofile-0-owner': unicode(owner1.auto_id),
815            'ownerprofile-0-age': u'55',
816        }
817        formset = FormSet(data, instance=owner1)
818        self.assertTrue(formset.is_valid())
819        saved = formset.save()
820        self.assertEqual(len(saved), 1)
821        profile1, = saved
822        self.assertEqual(profile1.owner, owner1)
823        self.assertEqual(profile1.age, 55)
824
825    def test_unique_true_enforces_max_num_one(self):
826        # ForeignKey with unique=True should enforce max_num=1
827
828        place = Place.objects.create(pk=1, name=u'Giordanos', city=u'Chicago')
829
830        FormSet = inlineformset_factory(Place, Location, can_delete=False)
831        self.assertEqual(FormSet.max_num, 1)
832
833        formset = FormSet(instance=place)
834        self.assertEqual(len(formset.forms), 1)
835        self.assertEqual(formset.forms[0].as_p(),
836            '<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'
837            '<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>')
838
839    def test_foreign_keys_in_parents(self):
840        self.assertEqual(type(_get_foreign_key(Restaurant, Owner)), models.ForeignKey)
841        self.assertEqual(type(_get_foreign_key(MexicanRestaurant, Owner)), models.ForeignKey)
842
843    def test_unique_validation(self):
844        FormSet = modelformset_factory(Product, extra=1)
845        data = {
846            'form-TOTAL_FORMS': '1',
847            'form-INITIAL_FORMS': '0',
848            'form-MAX_NUM_FORMS': '',
849            'form-0-slug': 'car-red',
850        }
851        formset = FormSet(data)
852        self.assertTrue(formset.is_valid())
853        saved = formset.save()
854        self.assertEqual(len(saved), 1)
855        product1, = saved
856        self.assertEqual(product1.slug, 'car-red')
857
858        data = {
859            'form-TOTAL_FORMS': '1',
860            'form-INITIAL_FORMS': '0',
861            'form-MAX_NUM_FORMS': '',
862            'form-0-slug': 'car-red',
863        }
864        formset = FormSet(data)
865        self.assertFalse(formset.is_valid())
866        self.assertEqual(formset.errors, [{'slug': [u'Product with this Slug already exists.']}])
867
868    def test_unique_together_validation(self):
869        FormSet = modelformset_factory(Price, extra=1)
870        data = {
871            'form-TOTAL_FORMS': '1',
872            'form-INITIAL_FORMS': '0',
873            'form-MAX_NUM_FORMS': '',
874            'form-0-price': u'12.00',
875            'form-0-quantity': '1',
876        }
877        formset = FormSet(data)
878        self.assertTrue(formset.is_valid())
879        saved = formset.save()
880        self.assertEqual(len(saved), 1)
881        price1, = saved
882        self.assertEqual(price1.price, Decimal('12.00'))
883        self.assertEqual(price1.quantity, 1)
884
885        data = {
886            'form-TOTAL_FORMS': '1',
887            'form-INITIAL_FORMS': '0',
888            'form-MAX_NUM_FORMS': '',
889            'form-0-price': u'12.00',
890            'form-0-quantity': '1',
891        }
892        formset = FormSet(data)
893        self.assertFalse(formset.is_valid())
894        self.assertEqual(formset.errors, [{'__all__': [u'Price with this Price and Quantity already exists.']}])
895
896    def test_unique_together_with_inlineformset_factory(self):
897        # Also see bug #8882.
898
899        repository = Repository.objects.create(name=u'Test Repo')
900        FormSet = inlineformset_factory(Repository, Revision, extra=1)
901        data = {
902            'revision_set-TOTAL_FORMS': '1',
903            'revision_set-INITIAL_FORMS': '0',
904            'revision_set-MAX_NUM_FORMS': '',
905            'revision_set-0-repository': repository.pk,
906            'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76',
907            'revision_set-0-DELETE': '',
908        }
909        formset = FormSet(data, instance=repository)
910        self.assertTrue(formset.is_valid())
911        saved = formset.save()
912        self.assertEqual(len(saved), 1)
913        revision1, = saved
914        self.assertEqual(revision1.repository, repository)
915        self.assertEqual(revision1.revision, '146239817507f148d448db38840db7c3cbf47c76')
916
917        # attempt to save the same revision against against the same repo.
918        data = {
919            'revision_set-TOTAL_FORMS': '1',
920            'revision_set-INITIAL_FORMS': '0',
921            'revision_set-MAX_NUM_FORMS': '',
922            'revision_set-0-repository': repository.pk,
923            'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76',
924            'revision_set-0-DELETE': '',
925        }
926        formset = FormSet(data, instance=repository)
927        self.assertFalse(formset.is_valid())
928        self.assertEqual(formset.errors, [{'__all__': [u'Revision with this Repository and Revision already exists.']}])
929
930        # unique_together with inlineformset_factory with overridden form fields
931        # Also see #9494
932
933        FormSet = inlineformset_factory(Repository, Revision, fields=('revision',), extra=1)
934        data = {
935            'revision_set-TOTAL_FORMS': '1',
936            'revision_set-INITIAL_FORMS': '0',
937            'revision_set-MAX_NUM_FORMS': '',
938            'revision_set-0-repository': repository.pk,
939            'reviā€¦

Large files files are truncated, but you can click here to view the full file