From fd8a7a5313f5e223212085b2e470e43c0047e066 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 30 Aug 2018 05:22:50 -0400 Subject: [PATCH] Fixed #29723 -- Fixed crash if InlineModelAdmin.has_add_permission() doesn't accept the obj argument. * Refs #27991 -- Added testing for ModelAdmin.get_inline_instances() if the inline's has_add_permission() doesn't accept 'obj'. * Fixed #29723 -- Fixed crash if InlineModelAdmin.has_add_permission() doesn't accept the obj argument. --- django/contrib/admin/options.py | 16 +++---- docs/releases/2.1.1.txt | 3 ++ ...test_has_add_permission_obj_deprecation.py | 46 +++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 0617ab91a8..fb2fcdd9d6 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -585,13 +585,8 @@ class ModelAdmin(BaseModelAdmin): inline_instances = [] for inline_class in self.inlines: inline = inline_class(self.model, self.admin_site) - # RemovedInDjango30Warning: obj will be a required argument. - args = get_func_args(inline.has_add_permission) - if 'obj' in args: - inline_has_add_permission = inline.has_add_permission(request, obj) - else: - inline_has_add_permission = inline.has_add_permission(request) if request: + inline_has_add_permission = inline._has_add_permission(request, obj) if not (inline.has_view_or_change_permission(request, obj) or inline_has_add_permission or inline.has_delete_permission(request, obj)): @@ -1484,7 +1479,7 @@ class ModelAdmin(BaseModelAdmin): for inline, formset in zip(inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request, obj)) readonly = list(inline.get_readonly_fields(request, obj)) - has_add_permission = inline.has_add_permission(request, obj) + has_add_permission = inline._has_add_permission(request, obj) has_change_permission = inline.has_change_permission(request, obj) has_delete_permission = inline.has_delete_permission(request, obj) has_view_permission = inline.has_view_permission(request, obj) @@ -2002,6 +1997,11 @@ class InlineModelAdmin(BaseModelAdmin): js.append('collapse%s.js' % extra) return forms.Media(js=['admin/js/%s' % url for url in js]) + def _has_add_permission(self, request, obj): + # RemovedInDjango30Warning: obj will be a required argument. + args = get_func_args(self.has_add_permission) + return self.has_add_permission(request, obj) if 'obj' in args else self.has_add_permission(request) + def get_extra(self, request, obj=None, **kwargs): """Hook for customizing the number of extra inline forms.""" return self.extra @@ -2047,7 +2047,7 @@ class InlineModelAdmin(BaseModelAdmin): base_model_form = defaults['form'] can_change = self.has_change_permission(request, obj) if request else True - can_add = self.has_add_permission(request, obj) if request else True + can_add = self._has_add_permission(request, obj) if request else True class DeleteProtectedModelForm(base_model_form): diff --git a/docs/releases/2.1.1.txt b/docs/releases/2.1.1.txt index 759d24c391..821c90aa68 100644 --- a/docs/releases/2.1.1.txt +++ b/docs/releases/2.1.1.txt @@ -46,3 +46,6 @@ Bugfixes ``values_list()`` after combining querysets with ``extra()`` with ``union()``, ``difference()``, or ``intersection()`` crashed due to mismatching columns (:ticket:`29694`). + +* Fixed crash if ``InlineModelAdmin.has_add_permission()`` doesn't accept the + ``obj`` argument (:ticket:`29723`). diff --git a/tests/modeladmin/test_has_add_permission_obj_deprecation.py b/tests/modeladmin/test_has_add_permission_obj_deprecation.py index 18448e94d7..f5489a41db 100644 --- a/tests/modeladmin/test_has_add_permission_obj_deprecation.py +++ b/tests/modeladmin/test_has_add_permission_obj_deprecation.py @@ -1,4 +1,8 @@ +from datetime import date + from django.contrib.admin.options import ModelAdmin, TabularInline +from django.contrib.admin.sites import AdminSite +from django.test import TestCase from django.utils.deprecation import RemovedInDjango30Warning from .models import Band, Song @@ -56,3 +60,45 @@ class HasAddPermissionObjTests(CheckTestCase): ) with self.assertWarnsMessage(RemovedInDjango30Warning, msg): self.assertIsValid(BandAdmin, Band) + + +class MockRequest: + method = 'POST' + FILES = {} + POST = {} + + +class SongInline(TabularInline): + model = Song + + def has_add_permission(self, request): + return True + + +class BandAdmin(ModelAdmin): + inlines = [SongInline] + + +class ModelAdminTests(TestCase): + + def setUp(self): + self.band = Band.objects.create(name='The Doors', bio='', sign_date=date(1965, 1, 1)) + self.song = Song.objects.create(name='test', band=self.band) + self.site = AdminSite() + self.request = MockRequest() + self.request.user = self.MockAddUser() + self.ma = BandAdmin(Band, self.site) + + class MockAddUser: + def has_perm(self, perm): + return perm == 'modeladmin.add_band' + + def test_get_inline_instances(self): + self.assertEqual(len(self.ma.get_inline_instances(self.request)), 1) + + def test_get_inline_formsets(self): + formsets, inline_instances = self.ma._create_formsets(self.request, self.band, change=True) + self.assertEqual(len(self.ma.get_inline_formsets(self.request, formsets, inline_instances)), 1) + + def test_get_formsets_with_inlines(self): + self.assertEqual(len(list(self.ma. get_formsets_with_inlines(self.request, self.band))), 1)