diff --git a/django/contrib/admin/media/js/inlines.js b/django/contrib/admin/media/js/inlines.js
index c45ce58..69ae74b 100644
a
|
b
|
|
26 | 26 | }; |
27 | 27 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); |
28 | 28 | var initialForms = $("#id_" + options.prefix + "-INITIAL_FORMS"); |
29 | | var maxForms = parseInt(totalForms.val()); |
| 29 | var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS"); |
30 | 30 | // only show the add button if we are allowed to add more items |
31 | | var showAddButton = (maxForms - parseInt(initialForms.val())) > 0; |
| 31 | var showAddButton = ((maxForms.val() == 0) || ((maxForms.val()-initialForms.val()) > 0)); |
32 | 32 | var selectedItems = this; |
33 | 33 | $(this).each(function(i) { |
34 | 34 | $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); |
35 | | // hide the extras, but only if there were no form errors |
36 | | if (!$(".errornote").html()) { |
37 | | var relatedItems = $(selectedItems).not("." + options.emptyCssClass); |
38 | | extraRows = relatedItems.length; |
39 | | if (parseInt(initialForms.val()) >= 0) { |
40 | | $(relatedItems).slice(initialForms.val()).remove(); |
41 | | } else { |
42 | | $(relatedItems).remove(); |
43 | | } |
44 | | totalForms.val(parseInt(initialForms.val())); |
45 | | } |
46 | 35 | }); |
47 | 36 | if ($(this).length && showAddButton) { |
48 | 37 | var addButton; |
… |
… |
|
58 | 47 | addButton = $(this).filter(":last").next().find("a"); |
59 | 48 | } |
60 | 49 | addButton.click(function() { |
61 | | var totalForms = parseInt($("#id_" + options.prefix + "-TOTAL_FORMS").val()); |
62 | | var initialForms = parseInt($("#id_" + options.prefix + "-INITIAL_FORMS").val()); |
63 | | var nextIndex = totalForms + 1; |
| 50 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); |
| 51 | var nextIndex = parseInt(totalForms.val()) + 1; |
64 | 52 | var template = $("#" + options.prefix + "-empty"); |
65 | 53 | var row = template.clone(true).get(0); |
66 | 54 | $(row).removeClass(options.emptyCssClass).removeAttr("id").insertBefore($(template)); |
… |
… |
|
79 | 67 | // last child element of the form's container: |
80 | 68 | $(row).children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>"); |
81 | 69 | } |
| 70 | $(row).find("input,select,textarea,label").each(function() { |
| 71 | updateElementIndex(this, options.prefix, totalForms.val()); |
| 72 | }); |
82 | 73 | // Update number of total forms |
83 | | $("#id_" + options.prefix + "-TOTAL_FORMS").val(nextIndex); |
84 | | // Hide add button in case we've hit the max |
85 | | if (maxForms <= nextIndex) { |
| 74 | $(totalForms).val(nextIndex); |
| 75 | // Hide add button in case we've hit the max, except we want to add infinitely |
| 76 | if ((maxForms.val() != 0) && (maxForms.val() <= totalForms.val())) { |
86 | 77 | addButton.parent().hide(); |
87 | 78 | } |
88 | 79 | // The delete button of each row triggers a bunch of other things |
… |
… |
|
96 | 87 | var forms = $("." + options.formCssClass); |
97 | 88 | $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); |
98 | 89 | // Show add button again once we drop below max |
99 | | if (maxForms >= forms.length) { |
| 90 | if ((maxForms.val() == 0) || (maxForms.val() >= forms.length)) { |
100 | 91 | addButton.parent().show(); |
101 | 92 | } |
102 | 93 | // Also, update names and ids for all remaining form controls |
… |
… |
|
108 | 99 | } |
109 | 100 | return false; |
110 | 101 | }); |
111 | | $(row).find("input,select,textarea,label").each(function() { |
112 | | updateElementIndex(this, options.prefix, totalForms); |
113 | | }); |
114 | 102 | // If a post-add callback was supplied, call it with the added form: |
115 | 103 | if (options.added) options.added($(row)); |
116 | 104 | return false; |
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 9dfdb8b..4e3cfdc 100644
a
|
b
|
class InlineModelAdmin(BaseModelAdmin):
|
1193 | 1193 | |
1194 | 1194 | def _media(self): |
1195 | 1195 | from django.conf import settings |
1196 | | js = ['js/jquery.min.js', 'js/inlines.min.js'] |
| 1196 | js = ['js/jquery.min.js', 'js/inlines.js'] |
1197 | 1197 | if self.prepopulated_fields: |
1198 | 1198 | js.append('js/urlify.js') |
1199 | 1199 | if self.filter_vertical or self.filter_horizontal: |
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index a86c18f..58af0ac 100644
a
|
b
|
__all__ = ('BaseFormSet', 'all_valid')
|
12 | 12 | # special field names |
13 | 13 | TOTAL_FORM_COUNT = 'TOTAL_FORMS' |
14 | 14 | INITIAL_FORM_COUNT = 'INITIAL_FORMS' |
| 15 | MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS' |
15 | 16 | ORDERING_FIELD_NAME = 'ORDER' |
16 | 17 | DELETION_FIELD_NAME = 'DELETE' |
17 | 18 | |
… |
… |
class ManagementForm(Form):
|
24 | 25 | def __init__(self, *args, **kwargs): |
25 | 26 | self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) |
26 | 27 | self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) |
| 28 | self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(widget=HiddenInput) |
27 | 29 | super(ManagementForm, self).__init__(*args, **kwargs) |
28 | 30 | |
29 | 31 | class BaseFormSet(StrAndUnicode): |
… |
… |
class BaseFormSet(StrAndUnicode):
|
56 | 58 | else: |
57 | 59 | form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={ |
58 | 60 | TOTAL_FORM_COUNT: self.total_form_count(), |
59 | | INITIAL_FORM_COUNT: self.initial_form_count() |
| 61 | INITIAL_FORM_COUNT: self.initial_form_count(), |
| 62 | MAX_NUM_FORM_COUNT: self.max_num |
60 | 63 | }) |
61 | 64 | return form |
62 | 65 | management_form = property(_management_form) |