diff --git a/django/contrib/admin/media/js/inlines.js b/django/contrib/admin/media/js/inlines.js index c45ce58bd2b..aae2b294d0e 100644 --- a/django/contrib/admin/media/js/inlines.js +++ b/django/contrib/admin/media/js/inlines.js @@ -20,29 +20,24 @@ var updateElementIndex = function(el, prefix, ndx) { var id_regex = new RegExp("(" + prefix + "-\\d+)"); var replacement = prefix + "-" + ndx; - if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); - if (el.id) el.id = el.id.replace(id_regex, replacement); - if (el.name) el.name = el.name.replace(id_regex, replacement); + if ($(el).attr("for")) { + $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); + } + if (el.id) { + el.id = el.id.replace(id_regex, replacement); + } + if (el.name) { + el.name = el.name.replace(id_regex, replacement); + } }; var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); var initialForms = $("#id_" + options.prefix + "-INITIAL_FORMS"); - var maxForms = parseInt(totalForms.val()); + var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS"); // only show the add button if we are allowed to add more items - var showAddButton = (maxForms - parseInt(initialForms.val())) > 0; + var showAddButton = ((maxForms.val() == 0) || ((maxForms.val()-totalForms.val()) > 0)); var selectedItems = this; $(this).each(function(i) { $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); - // hide the extras, but only if there were no form errors - if (!$(".errornote").html()) { - var relatedItems = $(selectedItems).not("." + options.emptyCssClass); - extraRows = relatedItems.length; - if (parseInt(initialForms.val()) >= 0) { - $(relatedItems).slice(initialForms.val()).remove(); - } else { - $(relatedItems).remove(); - } - totalForms.val(parseInt(initialForms.val())); - } }); if ($(this).length && showAddButton) { var addButton; @@ -58,9 +53,8 @@ addButton = $(this).filter(":last").next().find("a"); } addButton.click(function() { - var totalForms = parseInt($("#id_" + options.prefix + "-TOTAL_FORMS").val()); - var initialForms = parseInt($("#id_" + options.prefix + "-INITIAL_FORMS").val()); - var nextIndex = totalForms + 1; + var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); + var nextIndex = parseInt(totalForms.val()) + 1; var template = $("#" + options.prefix + "-empty"); var row = template.clone(true).get(0); $(row).removeClass(options.emptyCssClass).removeAttr("id").insertBefore($(template)); @@ -79,10 +73,13 @@ // last child element of the form's container: $(row).children(":first").append('' + options.deleteText + ""); } + $(row).find("input,select,textarea,label").each(function() { + updateElementIndex(this, options.prefix, totalForms.val()); + }); // Update number of total forms - $("#id_" + options.prefix + "-TOTAL_FORMS").val(nextIndex); - // Hide add button in case we've hit the max - if (maxForms <= nextIndex) { + $(totalForms).val(nextIndex); + // Hide add button in case we've hit the max, except we want to add infinitely + if ((maxForms.val() != 0) && (maxForms.val() <= totalForms.val())) { addButton.parent().hide(); } // The delete button of each row triggers a bunch of other things @@ -91,44 +88,45 @@ var row = $(this).parents("." + options.formCssClass); row.remove(); // If a post-delete callback was provided, call it with the deleted form: - if (options.removed) options.removed(row); + if (options.removed) { + options.removed(row); + } // Update the TOTAL_FORMS form count. var forms = $("." + options.formCssClass); $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); // Show add button again once we drop below max - if (maxForms >= forms.length) { + if ((maxForms.val() == 0) || (maxForms.val() >= forms.length)) { addButton.parent().show(); } // Also, update names and ids for all remaining form controls // so they remain in sequence: - for (var i=0, formCount=forms.length; i0;var e=this;a(this).each(function(m){a(this).not("."+k.emptyCssClass).addClass(k.formCssClass);if(!a(".errornote").html()){var l=a(e).not("."+k.emptyCssClass);extraRows=l.length;if(parseInt(c.val())>=0){a(l).slice(c.val()).remove()}else{a(l).remove()}f.val(parseInt(c.val()))}});if(a(this).length&&i){var g;if(a(this).attr("tagName")=="TR"){var d=this.eq(0).children().length;a(this).parent().append(''+k.addText+"");g=a(this).parent().find("tr:last a")}else{a(this).filter(":last").after('
'+k.addText+"
");g=a(this).filter(":last").next().find("a")}g.click(function(){var o=parseInt(a("#id_"+k.prefix+"-TOTAL_FORMS").val());var n=parseInt(a("#id_"+k.prefix+"-INITIAL_FORMS").val());var l=o+1;var m=a("#"+k.prefix+"-empty");var p=m.clone(true).get(0);a(p).removeClass(k.emptyCssClass).removeAttr("id").insertBefore(a(m));a(p).html(a(p).html().replace(/__prefix__/g,l));a(p).addClass(k.formCssClass).attr("id",k.prefix+l);if(a(p).is("TR")){a(p).children(":last").append('
'+k.deleteText+"
")}else{if(a(p).is("UL")||a(p).is("OL")){a(p).append('
  • '+k.deleteText+"
  • ")}else{a(p).children(":first").append(''+k.deleteText+"")}}a("#id_"+k.prefix+"-TOTAL_FORMS").val(l);if(h<=l){g.parent().hide()}a(p).find("a."+k.deleteCssClass).click(function(){var t=a(this).parents("."+k.formCssClass);t.remove();if(k.removed){k.removed(t)}var q=a("."+k.formCssClass);a("#id_"+k.prefix+"-TOTAL_FORMS").val(q.length);if(h>=q.length){g.parent().show()}for(var r=0,s=q.length;r0;a(this).each(function(){a(this).not("."+b.emptyCssClass).addClass(b.formCssClass)}); +if(a(this).length&&f){var i;if(a(this).attr("tagName")=="TR"){f=this.eq(0).children().length;a(this).parent().append(''+b.addText+"");i=a(this).parent().find("tr:last a")}else{a(this).filter(":last").after('");i=a(this).filter(":last").next().find("a")}i.click(function(){var d=a("#id_"+b.prefix+"-TOTAL_FORMS"),e=parseInt(d.val())+ +1,j=a("#"+b.prefix+"-empty"),c=j.clone(true).get(0);a(c).removeClass(b.emptyCssClass).removeAttr("id").insertBefore(a(j));a(c).html(a(c).html().replace(/__prefix__/g,e));a(c).addClass(b.formCssClass).attr("id",b.prefix+e);if(a(c).is("TR"))a(c).children(":last").append('
    '+b.deleteText+"
    ");else a(c).is("UL")||a(c).is("OL")?a(c).append('
  • '+b.deleteText+"
  • "):a(c).children(":first").append(''+b.deleteText+"");a(c).find("input,select,textarea,label").each(function(){l(this,b.prefix,d.val())});a(d).val(e);h.val()!=0&&h.val()<=d.val()&&i.parent().hide();a(c).find("a."+b.deleteCssClass).click(function(){var g=a(this).parents("."+b.formCssClass);g.remove();b.removed&&b.removed(g);g=a("."+b.formCssClass);a("#id_"+b.prefix+"-TOTAL_FORMS").val(g.length);if(h.val()==0||h.val()>=g.length)i.parent().show();for(var k=0,m=g.length;k \ No newline at end of file diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html index 96ea6b6843a..5c69bad24d8 100644 --- a/django/contrib/admin/templates/admin/edit_inline/tabular.html +++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html @@ -65,23 +65,24 @@ \ No newline at end of file diff --git a/django/forms/formsets.py b/django/forms/formsets.py index a86c18f1385..58af0ac23b8 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -12,6 +12,7 @@ __all__ = ('BaseFormSet', 'all_valid') # special field names TOTAL_FORM_COUNT = 'TOTAL_FORMS' INITIAL_FORM_COUNT = 'INITIAL_FORMS' +MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS' ORDERING_FIELD_NAME = 'ORDER' DELETION_FIELD_NAME = 'DELETE' @@ -24,6 +25,7 @@ class ManagementForm(Form): def __init__(self, *args, **kwargs): self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) + self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(widget=HiddenInput) super(ManagementForm, self).__init__(*args, **kwargs) class BaseFormSet(StrAndUnicode): @@ -56,7 +58,8 @@ class BaseFormSet(StrAndUnicode): else: form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={ TOTAL_FORM_COUNT: self.total_form_count(), - INITIAL_FORM_COUNT: self.initial_form_count() + INITIAL_FORM_COUNT: self.initial_form_count(), + MAX_NUM_FORM_COUNT: self.max_num }) return form management_form = property(_management_form) diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index 702523eb7c0..e8c3e98ac7c 100644 --- a/tests/modeltests/model_formsets/models.py +++ b/tests/modeltests/model_formsets/models.py @@ -200,6 +200,7 @@ __test__ = {'API_TESTS': """ >>> data = { ... 'form-TOTAL_FORMS': '3', # the number of forms rendered ... 'form-INITIAL_FORMS': '0', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-name': 'Charles Baudelaire', ... 'form-1-name': 'Arthur Rimbaud', ... 'form-2-name': '', @@ -237,6 +238,7 @@ them in alphabetical order by name. >>> data = { ... 'form-TOTAL_FORMS': '3', # the number of forms rendered ... 'form-INITIAL_FORMS': '2', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-id': '2', ... 'form-0-name': 'Arthur Rimbaud', ... 'form-1-id': '1', @@ -280,6 +282,7 @@ deltetion, make sure we don't save that form. >>> data = { ... 'form-TOTAL_FORMS': '4', # the number of forms rendered ... 'form-INITIAL_FORMS': '3', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-id': '2', ... 'form-0-name': 'Arthur Rimbaud', ... 'form-1-id': '1', @@ -309,6 +312,7 @@ Let's edit a record to ensure save only returns that one record. >>> data = { ... 'form-TOTAL_FORMS': '4', # the number of forms rendered ... 'form-INITIAL_FORMS': '3', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-id': '2', ... 'form-0-name': 'Walt Whitman', ... 'form-1-id': '1', @@ -339,6 +343,7 @@ Test the behavior of commit=False and save_m2m >>> data = { ... 'form-TOTAL_FORMS': '2', # the number of forms rendered ... 'form-INITIAL_FORMS': '1', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-id': '1', ... 'form-0-name': '2nd Tuesday of the Week Meeting', ... 'form-0-authors': [2, 1, 3, 4], @@ -393,6 +398,7 @@ used. >>> data = { ... 'form-TOTAL_FORMS': '3', # the number of forms rendered ... 'form-INITIAL_FORMS': '0', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-name': 'Walt Whitman', ... 'form-1-name': 'Charles Baudelaire', ... 'form-2-name': '', @@ -419,6 +425,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': '1', # the number of forms rendered ... 'form-INITIAL_FORMS': '0', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-author_ptr': '', ... 'form-0-name': 'Ernest Hemingway', ... 'form-0-write_speed': '10', @@ -442,6 +449,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': '2', # the number of forms rendered ... 'form-INITIAL_FORMS': '1', # the number of forms with initial data +... 'form-MAX_NUM_FORMS': '0', # the max number of forms ... 'form-0-author_ptr': hemingway_id, ... 'form-0-name': 'Ernest Hemingway', ... 'form-0-write_speed': '10', @@ -476,6 +484,7 @@ admin system's edit inline functionality works. >>> data = { ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered ... 'book_set-INITIAL_FORMS': '0', # the number of forms with initial data +... 'book_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'book_set-0-title': 'Les Fleurs du Mal', ... 'book_set-1-title': '', ... 'book_set-2-title': '', @@ -510,6 +519,7 @@ book. >>> data = { ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered ... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data +... 'book_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'book_set-0-id': '1', ... 'book_set-0-title': 'Les Fleurs du Mal', ... 'book_set-1-title': 'Les Paradis Artificiels', @@ -536,6 +546,7 @@ This is used in the admin for save_as functionality. >>> data = { ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered ... 'book_set-INITIAL_FORMS': '2', # the number of forms with initial data +... 'book_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'book_set-0-id': '1', ... 'book_set-0-title': 'Les Fleurs du Mal', ... 'book_set-1-id': '2', @@ -573,6 +584,7 @@ Test inline formsets where the inline-edited object has a custom primary key tha >>> data = { ... 'bookwithcustompk_set-TOTAL_FORMS': '1', # the number of forms rendered ... 'bookwithcustompk_set-INITIAL_FORMS': '0', # the number of forms with initial data +... 'bookwithcustompk_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'bookwithcustompk_set-0-my_pk': '77777', ... 'bookwithcustompk_set-0-title': 'Les Fleurs du Mal', ... } @@ -603,6 +615,7 @@ has a non AutoField yet auto-created primary key. >>> data = { ... 'alternatebook_set-TOTAL_FORMS': '1', # the number of forms rendered ... 'alternatebook_set-INITIAL_FORMS': '0', # the number of forms with initial data +... 'alternatebook_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'alternatebook_set-0-title': 'Flowers of Evil', ... 'alternatebook_set-0-notes': 'English translation of Les Fleurs du Mal' ... } @@ -631,6 +644,7 @@ True >>> data = { ... 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered ... 'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data +... 'poem_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'poem_set-0-name': 'The Cloud in Trousers', ... 'poem_set-1-name': 'I', ... 'poem_set-2-name': '', @@ -659,6 +673,7 @@ We can provide a custom queryset to our InlineFormSet: >>> data = { ... 'book_set-TOTAL_FORMS': '5', # the number of forms rendered ... 'book_set-INITIAL_FORMS': '3', # the number of forms with initial data +... 'book_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'book_set-0-id': '1', ... 'book_set-0-title': 'Les Fleurs du Mal', ... 'book_set-1-id': '2', @@ -682,6 +697,7 @@ True >>> data = { ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered ... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data +... 'book_set-MAX_NUM_FORMS': '0', # the max number of forms ... 'book_set-0-id': '5', ... 'book_set-0-title': 'Flowers of Evil', ... 'book_set-1-title': 'Revue des deux mondes', @@ -718,6 +734,7 @@ We need to ensure that it is displayed >>> data = { ... 'owner_set-TOTAL_FORMS': '2', ... 'owner_set-INITIAL_FORMS': '0', +... 'owner_set-MAX_NUM_FORMS': '0', ... 'owner_set-0-auto_id': '', ... 'owner_set-0-name': u'Joe Perry', ... 'owner_set-1-auto_id': '', @@ -739,6 +756,7 @@ True >>> data = { ... 'owner_set-TOTAL_FORMS': '3', ... 'owner_set-INITIAL_FORMS': '1', +... 'owner_set-MAX_NUM_FORMS': '0', ... 'owner_set-0-auto_id': u'1', ... 'owner_set-0-name': u'Joe Perry', ... 'owner_set-1-auto_id': '', @@ -767,7 +785,8 @@ True >>> owner = Owner.objects.get(name=u'Joe Perry') >>> FormSet = inlineformset_factory(Owner, OwnerProfile, max_num=1, can_delete=False) - +>>> FormSet.max_num +1 >>> formset = FormSet(instance=owner) >>> for form in formset.forms: ... print form.as_p() @@ -776,6 +795,7 @@ True >>> data = { ... 'ownerprofile-TOTAL_FORMS': '1', ... 'ownerprofile-INITIAL_FORMS': '0', +... 'ownerprofile-MAX_NUM_FORMS': '1', ... 'ownerprofile-0-owner': '', ... 'ownerprofile-0-age': u'54', ... } @@ -784,7 +804,6 @@ True True >>> formset.save() [] - >>> formset = FormSet(instance=owner) >>> for form in formset.forms: ... print form.as_p() @@ -793,6 +812,7 @@ True >>> data = { ... 'ownerprofile-TOTAL_FORMS': '1', ... 'ownerprofile-INITIAL_FORMS': '1', +... 'ownerprofile-MAX_NUM_FORMS': '1', ... 'ownerprofile-0-owner': u'1', ... 'ownerprofile-0-age': u'55', ... } @@ -805,6 +825,8 @@ True # ForeignKey with unique=True should enforce max_num=1 >>> FormSet = inlineformset_factory(Place, Location, can_delete=False) +>>> FormSet.max_num +1 >>> formset = FormSet(instance=place) >>> for form in formset.forms: ... print form.as_p() @@ -826,6 +848,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': '1', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-slug': 'car-red', ... } >>> formset = FormSet(data) @@ -837,6 +860,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': '1', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-slug': 'car-red', ... } >>> formset = FormSet(data) @@ -851,6 +875,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': '1', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-price': u'12.00', ... 'form-0-quantity': '1', ... } @@ -863,6 +888,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': '1', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-price': u'12.00', ... 'form-0-quantity': '1', ... } @@ -880,6 +906,7 @@ False >>> data = { ... 'revision_set-TOTAL_FORMS': '1', ... 'revision_set-INITIAL_FORMS': '0', +... 'revision_set-MAX_NUM_FORMS': '0', ... 'revision_set-0-repository': repository.pk, ... 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', ... 'revision_set-0-DELETE': '', @@ -894,6 +921,7 @@ True >>> data = { ... 'revision_set-TOTAL_FORMS': '1', ... 'revision_set-INITIAL_FORMS': '0', +... 'revision_set-MAX_NUM_FORMS': '0', ... 'revision_set-0-repository': repository.pk, ... 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', ... 'revision_set-0-DELETE': '', @@ -911,6 +939,7 @@ False >>> data = { ... 'revision_set-TOTAL_FORMS': '1', ... 'revision_set-INITIAL_FORMS': '0', +... 'revision_set-MAX_NUM_FORMS': '0', ... 'revision_set-0-repository': repository.pk, ... 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', ... 'revision_set-0-DELETE': '', @@ -940,6 +969,7 @@ False >>> data = { ... 'membership_set-TOTAL_FORMS': '1', ... 'membership_set-INITIAL_FORMS': '0', +... 'membership_set-MAX_NUM_FORMS': '0', ... 'membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), ... 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), ... 'membership_set-0-karma': '', @@ -954,6 +984,7 @@ True >>> filled_data = { ... 'membership_set-TOTAL_FORMS': '1', ... 'membership_set-INITIAL_FORMS': '0', +... 'membership_set-MAX_NUM_FORMS': '0', ... 'membership_set-0-date_joined': unicode(one_day_later.strftime('%Y-%m-%d %H:%M:%S')), ... 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), ... 'membership_set-0-karma': '', @@ -976,6 +1007,7 @@ False >>> data = { ... 'membership_set-TOTAL_FORMS': '1', ... 'membership_set-INITIAL_FORMS': '0', +... 'membership_set-MAX_NUM_FORMS': '0', ... 'membership_set-0-date_joined_0': unicode(now.strftime('%Y-%m-%d')), ... 'membership_set-0-date_joined_1': unicode(now.strftime('%H:%M:%S')), ... 'initial-membership_set-0-date_joined': unicode(now.strftime('%Y-%m-%d %H:%M:%S')), @@ -1011,6 +1043,7 @@ True >>> data = { ... 'form-TOTAL_FORMS': 2, ... 'form-INITIAL_FORMS': 0, +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-slug': 'red_car', ... 'form-1-slug': 'red_car', ... } @@ -1024,6 +1057,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': 2, ... 'form-INITIAL_FORMS': 0, +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-price': '25', ... 'form-0-quantity': '7', ... 'form-1-price': '25', @@ -1041,6 +1075,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... 'form-0-price': '24', ... 'form-1-price': '24', ... } @@ -1054,6 +1089,7 @@ True >>> data = { ... 'book_set-TOTAL_FORMS': '2', ... 'book_set-INITIAL_FORMS': '2', +... 'book_set-MAX_NUM_FORMS': '0', ... ... 'book_set-0-title': 'The 2008 Election', ... 'book_set-0-author': str(author.id), @@ -1075,6 +1111,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... ... 'form-0-title': 'blah', ... 'form-0-slug': 'Morning', @@ -1096,6 +1133,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... ... 'form-0-title': 'foo', ... 'form-0-slug': 'Morning in Prague', @@ -1115,6 +1153,7 @@ False >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', +... 'form-MAX_NUM_FORMS': '0', ... ... 'form-0-title': 'foo', ... 'form-0-slug': 'Morning in Prague', diff --git a/tests/modeltests/model_formsets/tests.py b/tests/modeltests/model_formsets/tests.py index d343fc72966..62489bad378 100644 --- a/tests/modeltests/model_formsets/tests.py +++ b/tests/modeltests/model_formsets/tests.py @@ -9,6 +9,7 @@ class DeletionTests(TestCase): data = { 'form-TOTAL_FORMS': u'1', 'form-INITIAL_FORMS': u'1', + 'form-MAX_NUM_FORMS': u'0', 'form-0-id': str(poet.pk), 'form-0-name': u'test', 'form-0-DELETE': u'on', @@ -27,6 +28,7 @@ class DeletionTests(TestCase): data = { 'form-TOTAL_FORMS': u'1', 'form-INITIAL_FORMS': u'0', + 'form-MAX_NUM_FORMS': u'0', 'form-0-id': u'', 'form-0-name': u'x' * 1000, } @@ -53,6 +55,7 @@ class DeletionTests(TestCase): data = { 'form-TOTAL_FORMS': u'1', 'form-INITIAL_FORMS': u'1', + 'form-MAX_NUM_FORMS': u'0', 'form-0-id': u'1', 'form-0-name': u'x' * 1000, } diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 7fc52b3460a..d52a57f9ee3 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -87,6 +87,7 @@ class AdminViewBasicTest(TestCase): # inline data "article_set-TOTAL_FORMS": u"3", "article_set-INITIAL_FORMS": u"0", + "article_set-MAX_NUM_FORMS": u"0", } response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere @@ -97,6 +98,7 @@ class AdminViewBasicTest(TestCase): # inline data "article_set-TOTAL_FORMS": u"6", "article_set-INITIAL_FORMS": u"3", + "article_set-MAX_NUM_FORMS": u"0", "article_set-0-id": u"1", # there is no title in database, give one here or formset will fail. "article_set-0-title": u"Norske bostaver æøå skaper problemer", @@ -864,6 +866,7 @@ class AdminViewUnicodeTest(TestCase): # inline data "chapter_set-TOTAL_FORMS": u"6", "chapter_set-INITIAL_FORMS": u"3", + "chapter_set-MAX_NUM_FORMS": u"0", "chapter_set-0-id": u"1", "chapter_set-0-title": u"Norske bostaver æøå skaper problemer", "chapter_set-0-content": u"<p>Svært frustrerende med UnicodeDecodeError</p>", @@ -926,14 +929,14 @@ class AdminViewListEditable(TestCase): def test_changelist_input_html(self): response = self.client.get('/test_admin/admin/admin_views/person/') # 2 inputs per object(the field and the hidden id field) = 6 - # 2 management hidden fields = 2 + # 3 management hidden fields = 3 # 4 action inputs (3 regular checkboxes, 1 checkbox to select all) # main form submit button = 1 # search field and search submit button = 2 # CSRF field = 1 # field to track 'select all' across paginated views = 1 - # 6 + 2 + 4 + 1 + 2 + 1 + 1 = 17 inputs - self.failUnlessEqual(response.content.count("