| 1 | diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
|
|---|
| 2 | index 7373837..487fc4e 100644
|
|---|
| 3 | --- a/django/contrib/admin/options.py
|
|---|
| 4 | +++ b/django/contrib/admin/options.py
|
|---|
| 5 | @@ -13,7 +13,7 @@ from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_o
|
|---|
| 6 | from django.contrib.admin.templatetags.admin_static import static
|
|---|
| 7 | from django.contrib import messages
|
|---|
| 8 | from django.views.decorators.csrf import csrf_protect
|
|---|
| 9 | -from django.core.exceptions import PermissionDenied, ValidationError, FieldError
|
|---|
| 10 | +from django.core.exceptions import PermissionDenied, ValidationError, FieldError, ImproperlyConfigured
|
|---|
| 11 | from django.core.paginator import Paginator
|
|---|
| 12 | from django.core.urlresolvers import reverse
|
|---|
| 13 | from django.db import models, transaction, router
|
|---|
| 14 | @@ -1467,6 +1467,17 @@ class InlineModelAdmin(BaseModelAdmin):
|
|---|
| 15 | js.extend(['SelectBox.js', 'SelectFilter2.js'])
|
|---|
| 16 | return forms.Media(js=[static('admin/js/%s' % url) for url in js])
|
|---|
| 17 |
|
|---|
| 18 | + def get_extra(self, request, obj=None, **kwargs):
|
|---|
| 19 | + """Get the number of extra inline forms needed"""
|
|---|
| 20 | + if not isinstance(self.extra, int):
|
|---|
| 21 | + raise ImproperlyConfigured("'%s.extra' should be a integer."
|
|---|
| 22 | + % self.__class__.__name__)
|
|---|
| 23 | + return self.extra
|
|---|
| 24 | +
|
|---|
| 25 | + def get_max_num(self, request, obj=None, **kwargs):
|
|---|
| 26 | + """Get the max number of extra inline forms needed"""
|
|---|
| 27 | + return self.max_num
|
|---|
| 28 | +
|
|---|
| 29 | def get_formset(self, request, obj=None, **kwargs):
|
|---|
| 30 | """Returns a BaseInlineFormSet class for use in admin add/change views."""
|
|---|
| 31 | if self.declared_fieldsets:
|
|---|
| 32 | @@ -1493,8 +1504,8 @@ class InlineModelAdmin(BaseModelAdmin):
|
|---|
| 33 | "fields": fields,
|
|---|
| 34 | "exclude": exclude,
|
|---|
| 35 | "formfield_callback": partial(self.formfield_for_dbfield, request=request),
|
|---|
| 36 | - "extra": self.extra,
|
|---|
| 37 | - "max_num": self.max_num,
|
|---|
| 38 | + "extra": self.get_extra(request, obj, *kwargs),
|
|---|
| 39 | + "max_num": self.get_max_num(request, obj, *kwargs),
|
|---|
| 40 | "can_delete": can_delete,
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
|
|---|
| 44 | index 8d65f96..06b5055 100644
|
|---|
| 45 | --- a/django/contrib/admin/validation.py
|
|---|
| 46 | +++ b/django/contrib/admin/validation.py
|
|---|
| 47 | @@ -199,11 +199,6 @@ def validate_inline(cls, parent, parent_model):
|
|---|
| 48 |
|
|---|
| 49 | fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
|
|---|
| 50 |
|
|---|
| 51 | - # extra = 3
|
|---|
| 52 | - if not isinstance(cls.extra, int):
|
|---|
| 53 | - raise ImproperlyConfigured("'%s.extra' should be a integer."
|
|---|
| 54 | - % cls.__name__)
|
|---|
| 55 | -
|
|---|
| 56 | # max_num = None
|
|---|
| 57 | max_num = getattr(cls, 'max_num', None)
|
|---|
| 58 | if max_num is not None and not isinstance(max_num, int):
|
|---|
| 59 | diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py
|
|---|
| 60 | index 44671d0..2f88248 100644
|
|---|
| 61 | --- a/tests/admin_inlines/admin.py
|
|---|
| 62 | +++ b/tests/admin_inlines/admin.py
|
|---|
| 63 | @@ -129,6 +129,22 @@ class ChildModel1Inline(admin.TabularInline):
|
|---|
| 64 | class ChildModel2Inline(admin.StackedInline):
|
|---|
| 65 | model = ChildModel2
|
|---|
| 66 |
|
|---|
| 67 | +# admin for #19425 and #18388
|
|---|
| 68 | +class BinaryTreeAdmin(admin.TabularInline):
|
|---|
| 69 | + model = BinaryTree
|
|---|
| 70 | +
|
|---|
| 71 | + def get_extra(self, request, obj=None, **kwargs):
|
|---|
| 72 | + extra = 2
|
|---|
| 73 | + if obj:
|
|---|
| 74 | + return extra - obj.binarytree_set.count()
|
|---|
| 75 | + return extra
|
|---|
| 76 | +
|
|---|
| 77 | + def get_max_num(self, request, obj=None, **kwargs):
|
|---|
| 78 | + max_num = 3
|
|---|
| 79 | + if obj:
|
|---|
| 80 | + return max_num - obj.binarytree_set.count()
|
|---|
| 81 | + return max_num
|
|---|
| 82 | +
|
|---|
| 83 | # admin for #19524
|
|---|
| 84 | class SightingInline(admin.TabularInline):
|
|---|
| 85 | model = Sighting
|
|---|
| 86 | @@ -150,4 +166,5 @@ site.register(Author, AuthorAdmin)
|
|---|
| 87 | site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline, ReadOnlyInlineInline])
|
|---|
| 88 | site.register(ProfileCollection, inlines=[ProfileInline])
|
|---|
| 89 | site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2Inline])
|
|---|
| 90 | +site.register(BinaryTree, inlines=[BinaryTreeAdmin])
|
|---|
| 91 | site.register(ExtraTerrestrial, inlines=[SightingInline])
|
|---|
| 92 | diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py
|
|---|
| 93 | index 82c1c3f..d4ba0ab 100644
|
|---|
| 94 | --- a/tests/admin_inlines/models.py
|
|---|
| 95 | +++ b/tests/admin_inlines/models.py
|
|---|
| 96 | @@ -183,6 +183,12 @@ class ChildModel2(models.Model):
|
|---|
| 97 | def get_absolute_url(self):
|
|---|
| 98 | return '/child_model2/'
|
|---|
| 99 |
|
|---|
| 100 | +
|
|---|
| 101 | +# Models for #19425
|
|---|
| 102 | +class BinaryTree(models.Model):
|
|---|
| 103 | + name = models.CharField(max_length=100)
|
|---|
| 104 | + parent = models.ForeignKey('self', null=True, blank=True)
|
|---|
| 105 | +
|
|---|
| 106 | # Models for #19524
|
|---|
| 107 |
|
|---|
| 108 | class LifeForm(models.Model):
|
|---|