diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 7373837..487fc4e 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -13,7 +13,7 @@ from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_o from django.contrib.admin.templatetags.admin_static import static from django.contrib import messages from django.views.decorators.csrf import csrf_protect -from django.core.exceptions import PermissionDenied, ValidationError, FieldError +from django.core.exceptions import PermissionDenied, ValidationError, FieldError, ImproperlyConfigured from django.core.paginator import Paginator from django.core.urlresolvers import reverse from django.db import models, transaction, router @@ -1467,6 +1467,17 @@ class InlineModelAdmin(BaseModelAdmin): js.extend(['SelectBox.js', 'SelectFilter2.js']) return forms.Media(js=[static('admin/js/%s' % url) for url in js]) + def get_extra(self, request, obj=None, **kwargs): + """Get the number of extra inline forms needed""" + if not isinstance(self.extra, int): + raise ImproperlyConfigured("'%s.extra' should be a integer." + % self.__class__.__name__) + return self.extra + + def get_max_num(self, request, obj=None, **kwargs): + """Get the max number of extra inline forms needed""" + return self.max_num + def get_formset(self, request, obj=None, **kwargs): """Returns a BaseInlineFormSet class for use in admin add/change views.""" if self.declared_fieldsets: @@ -1493,8 +1504,8 @@ class InlineModelAdmin(BaseModelAdmin): "fields": fields, "exclude": exclude, "formfield_callback": partial(self.formfield_for_dbfield, request=request), - "extra": self.extra, - "max_num": self.max_num, + "extra": self.get_extra(request, obj, *kwargs), + "max_num": self.get_max_num(request, obj, *kwargs), "can_delete": can_delete, } diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 8d65f96..06b5055 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -199,11 +199,6 @@ def validate_inline(cls, parent, parent_model): fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True) - # extra = 3 - if not isinstance(cls.extra, int): - raise ImproperlyConfigured("'%s.extra' should be a integer." - % cls.__name__) - # max_num = None max_num = getattr(cls, 'max_num', None) if max_num is not None and not isinstance(max_num, int): diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index 44671d0..2f88248 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -129,6 +129,22 @@ class ChildModel1Inline(admin.TabularInline): class ChildModel2Inline(admin.StackedInline): model = ChildModel2 +# admin for #19425 and #18388 +class BinaryTreeAdmin(admin.TabularInline): + model = BinaryTree + + def get_extra(self, request, obj=None, **kwargs): + extra = 2 + if obj: + return extra - obj.binarytree_set.count() + return extra + + def get_max_num(self, request, obj=None, **kwargs): + max_num = 3 + if obj: + return max_num - obj.binarytree_set.count() + return max_num + # admin for #19524 class SightingInline(admin.TabularInline): model = Sighting @@ -150,4 +166,5 @@ site.register(Author, AuthorAdmin) site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline, ReadOnlyInlineInline]) site.register(ProfileCollection, inlines=[ProfileInline]) site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2Inline]) +site.register(BinaryTree, inlines=[BinaryTreeAdmin]) site.register(ExtraTerrestrial, inlines=[SightingInline]) diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py index 82c1c3f..d4ba0ab 100644 --- a/tests/admin_inlines/models.py +++ b/tests/admin_inlines/models.py @@ -183,6 +183,12 @@ class ChildModel2(models.Model): def get_absolute_url(self): return '/child_model2/' + +# Models for #19425 +class BinaryTree(models.Model): + name = models.CharField(max_length=100) + parent = models.ForeignKey('self', null=True, blank=True) + # Models for #19524 class LifeForm(models.Model):