parts/django/tests/modeltests/model_formsets/tests.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     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.'])