diff -r 2d5cd6f1136e django/contrib/admin/options.py
a
|
b
|
|
830 | 830 | formset = FormSet(request.POST, request.FILES, |
831 | 831 | instance=new_object, prefix=prefix) |
832 | 832 | formsets.append(formset) |
| 833 | for inline in self.inline_instances: |
| 834 | # If this is the inline that matches this formset, and |
| 835 | # we have some nested inlines to deal with, then we need |
| 836 | # to get the relevant formset for each of the forms in |
| 837 | # the current formset. |
| 838 | if inline.inlines and inline.model == formset.model: |
| 839 | for nested in inline.inline_instances: |
| 840 | for the_form in formset.forms: |
| 841 | InlineFormSet = nested.get_formset(request, the_form.instance) |
| 842 | prefix = "%s-%s" % (the_form.prefix, InlineFormSet.get_default_prefix()) |
| 843 | formsets.append(InlineFormSet(request.POST, request.FILES, instance=the_form.instance, prefix=prefix)) |
833 | 844 | |
834 | 845 | if all_valid(formsets) and form_validated: |
835 | 846 | self.save_model(request, new_object, form, change=True) |
… |
… |
|
859 | 870 | for inline, formset in zip(self.inline_instances, formsets): |
860 | 871 | fieldsets = list(inline.get_fieldsets(request, obj)) |
861 | 872 | inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets) |
| 873 | if inline.inlines: |
| 874 | for form in formset.forms: |
| 875 | if form.instance.pk: |
| 876 | instance = form.instance |
| 877 | else: |
| 878 | instance = None |
| 879 | form.inlines = inline.get_inlines(request, instance, prefix=form.prefix) |
| 880 | inline_admin_formset.inlines = inline.get_inlines(request) |
862 | 881 | inline_admin_formsets.append(inline_admin_formset) |
863 | 882 | media = media + inline_admin_formset.media |
864 | 883 | |
… |
… |
|
1119 | 1138 | template = None |
1120 | 1139 | verbose_name = None |
1121 | 1140 | verbose_name_plural = None |
| 1141 | inlines = [] |
1122 | 1142 | |
1123 | 1143 | def __init__(self, parent_model, admin_site): |
1124 | 1144 | self.admin_site = admin_site |
… |
… |
|
1129 | 1149 | self.verbose_name = self.model._meta.verbose_name |
1130 | 1150 | if self.verbose_name_plural is None: |
1131 | 1151 | self.verbose_name_plural = self.model._meta.verbose_name_plural |
| 1152 | self.inline_instances = [] |
| 1153 | for inline_class in self.inlines: |
| 1154 | inline_instance = inline_class(self.model, self.admin_site) |
| 1155 | self.inline_instances.append(inline_instance) |
1132 | 1156 | |
1133 | 1157 | def _media(self): |
1134 | 1158 | from django.conf import settings |
… |
… |
|
1171 | 1195 | form = self.get_formset(request).form |
1172 | 1196 | return [(None, {'fields': form.base_fields.keys()})] |
1173 | 1197 | |
| 1198 | def get_inlines(self, request, obj=None, prefix=None): |
| 1199 | nested_inlines = [] |
| 1200 | for inline in self.inline_instances: |
| 1201 | FormSet = inline.get_formset(request, obj) |
| 1202 | prefix = "%s-%s" % (prefix, FormSet.get_default_prefix()) |
| 1203 | formset = FormSet(instance=obj, prefix=prefix) |
| 1204 | fieldsets = list(inline.get_fieldsets(request, obj)) |
| 1205 | nested_inline = helpers.InlineAdminFormSet(inline, formset, fieldsets) |
| 1206 | nested_inlines.append(nested_inline) |
| 1207 | return nested_inlines |
| 1208 | |
| 1209 | |
1174 | 1210 | class StackedInline(InlineModelAdmin): |
1175 | 1211 | template = 'admin/edit_inline/stacked.html' |
1176 | 1212 | |
diff -r 2d5cd6f1136e django/contrib/admin/templates/admin/edit_inline/nested.html
-
|
+
|
|
| 1 | <td> |
| 2 | {{ nested.formset.management_form }} |
| 3 | <table> |
| 4 | <thead> |
| 5 | <tr> |
| 6 | {% for field in nested.fields %} |
| 7 | <th {% if forloop.first %}colspan="2"{% endif %}>{{ field.label|capfirst }}</th> |
| 8 | {% endfor %} |
| 9 | {% if nested.formset.can_delete %} |
| 10 | <th>Delete?</th> |
| 11 | {% endif %} |
| 12 | </tr> |
| 13 | </thead> |
| 14 | |
| 15 | <tbody> |
| 16 | {% for formset in nested %} |
| 17 | {% if formset.form.non_field_errors %} |
| 18 | <tr><td colspan="{{ formset.field_count }}"> |
| 19 | {{ form.form.non_field_errors }} |
| 20 | </td></tr> |
| 21 | {% endif %} |
| 22 | <tr class="{% if formset.original %}has_original{% endif %}"> |
| 23 | <td class="original"> |
| 24 | {% if formset.original %}<p style="position:relative;"> |
| 25 | {{ formset.original }} |
| 26 | </p>{% endif %} |
| 27 | {% if formset.has_auto_field %} |
| 28 | {{ formset.pk_field.field }} |
| 29 | {% endif %}{{ formset.fk_field.field }} |
| 30 | </td> |
| 31 | {% for fieldset in formset %} |
| 32 | {% for line in fieldset %} |
| 33 | {% for field in line %} |
| 34 | <td class="{{ field.field.name }}"> |
| 35 | {{ field.field.errors.as_ul}} |
| 36 | {{ field.field }} |
| 37 | </td> |
| 38 | {% endfor %} |
| 39 | {% endfor %} |
| 40 | {% endfor %} |
| 41 | {% if formset.original and nested.formset.can_delete %} |
| 42 | <td class="delete">{{ formset.deletion_field.field }}</td> |
| 43 | {% endif %} |
| 44 | </tr> |
| 45 | {% endfor %} |
| 46 | </tbody> |
| 47 | </table> |
| 48 | </td> |
| 49 | No newline at end of file |
diff -r 2d5cd6f1136e django/contrib/admin/templates/admin/edit_inline/tabular.html
a
|
b
|
|
13 | 13 | {% endif %} |
14 | 14 | {% endfor %} |
15 | 15 | {% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %} |
| 16 | {% if inline_admin_formset.inlines %} |
| 17 | {% for nested in inline_admin_formset.inlines %} |
| 18 | <th>{{ nested.opts.verbose_name_plural|capfirst }}</th> |
| 19 | {% endfor %} |
| 20 | {% endif %} |
16 | 21 | </tr></thead> |
17 | 22 | |
18 | 23 | <tbody> |
… |
… |
|
21 | 26 | <tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr> |
22 | 27 | {% endif %} |
23 | 28 | <tr class="{% cycle row1,row2 %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}"> |
24 | | |
| 29 | |
25 | 30 | <td class="original"> |
26 | 31 | {% if inline_admin_form.original or inline_admin_form.show_url %}<p> |
27 | 32 | {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %} |
… |
… |
|
54 | 59 | {% if inline_admin_formset.formset.can_delete %} |
55 | 60 | <td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td> |
56 | 61 | {% endif %} |
57 | | |
| 62 | |
| 63 | {% if inline_admin_formset.inlines %} |
| 64 | {% for nested in inline_admin_form.form.inlines %} |
| 65 | {% include 'admin/edit_inline/nested.html' %} |
| 66 | {% endfor %} |
| 67 | {% endif %} |
58 | 68 | </tr> |
59 | | |
60 | 69 | {% endfor %} |
61 | 70 | </tbody> |
62 | 71 | </table> |
diff -r 2d5cd6f1136e django/contrib/admin/validation.py
a
|
b
|
|
177 | 177 | raise ImproperlyConfigured("%s cannot exclude the field " |
178 | 178 | "'%s' - this is the foreign key to the parent model " |
179 | 179 | "%s." % (cls.__name__, fk.name, parent_model.__name__)) |
| 180 | |
| 181 | # nested inlines |
| 182 | # inlines = [] |
| 183 | if hasattr(cls, 'inlines'): |
| 184 | check_isseq(cls, 'inlines', cls.inlines) |
| 185 | for idx, inline in enumerate(cls.inlines): |
| 186 | if not issubclass(inline, BaseModelAdmin): |
| 187 | raise ImproperlyConfigured("'%s.inlines[%d]' does not inherit" |
| 188 | " from BaseModelAdmin." % (cls.__name__, idx)) |
| 189 | if not inline.model: |
| 190 | raise ImproperlyConfigured("'model' is a required attribute " |
| 191 | "of '%s.inlines[%d]'." % (cls.__name__, idx)) |
| 192 | if not issubclass(inline.model, models.Model): |
| 193 | raise ImproperlyConfigured("'%s.inlines[%s].model' does not " |
| 194 | "inherit from models.Model." % (cls.__name__, idx)) |
| 195 | validate_base(inline, inline.model) |
| 196 | validate_inline(inline, cls, cls.model) |
180 | 197 | |
181 | 198 | def validate_base(cls, model): |
182 | 199 | opts = model._meta |