Refs #27991 -- Made obj a required argument of InlineModelAdmin.has_add_permission().
Per deprecation timeline.
This commit is contained in:
parent
92d4d0859a
commit
6079ed82f4
|
@ -1,4 +1,3 @@
|
||||||
import warnings
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
@ -16,8 +15,6 @@ from django.forms.models import (
|
||||||
)
|
)
|
||||||
from django.template import engines
|
from django.template import engines
|
||||||
from django.template.backends.django import DjangoTemplates
|
from django.template.backends.django import DjangoTemplates
|
||||||
from django.utils.deprecation import RemovedInDjango30Warning
|
|
||||||
from django.utils.inspect import get_func_args
|
|
||||||
|
|
||||||
|
|
||||||
def _issubclass(cls, classinfo):
|
def _issubclass(cls, classinfo):
|
||||||
|
@ -981,7 +978,6 @@ class ModelAdminChecks(BaseModelAdminChecks):
|
||||||
class InlineModelAdminChecks(BaseModelAdminChecks):
|
class InlineModelAdminChecks(BaseModelAdminChecks):
|
||||||
|
|
||||||
def check(self, inline_obj, **kwargs):
|
def check(self, inline_obj, **kwargs):
|
||||||
self._check_has_add_permission(inline_obj)
|
|
||||||
parent_model = inline_obj.parent_model
|
parent_model = inline_obj.parent_model
|
||||||
return [
|
return [
|
||||||
*super().check(inline_obj),
|
*super().check(inline_obj),
|
||||||
|
@ -1066,20 +1062,6 @@ class InlineModelAdminChecks(BaseModelAdminChecks):
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _check_has_add_permission(self, obj):
|
|
||||||
cls = obj.__class__
|
|
||||||
try:
|
|
||||||
func = cls.has_add_permission
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
args = get_func_args(func)
|
|
||||||
if 'obj' not in args:
|
|
||||||
warnings.warn(
|
|
||||||
"Update %s.has_add_permission() to accept a positional "
|
|
||||||
"`obj` argument." % cls.__name__, RemovedInDjango30Warning
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def must_be(type, option, obj, id):
|
def must_be(type, option, obj, id):
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -45,7 +45,6 @@ from django.urls import reverse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from django.utils.inspect import get_func_args
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.text import capfirst, format_lazy, get_text_list
|
from django.utils.text import capfirst, format_lazy, get_text_list
|
||||||
from django.utils.translation import gettext as _, ngettext
|
from django.utils.translation import gettext as _, ngettext
|
||||||
|
@ -587,12 +586,11 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
for inline_class in self.inlines:
|
for inline_class in self.inlines:
|
||||||
inline = inline_class(self.model, self.admin_site)
|
inline = inline_class(self.model, self.admin_site)
|
||||||
if request:
|
if request:
|
||||||
inline_has_add_permission = inline._has_add_permission(request, obj)
|
|
||||||
if not (inline.has_view_or_change_permission(request, obj) or
|
if not (inline.has_view_or_change_permission(request, obj) or
|
||||||
inline_has_add_permission or
|
inline.has_add_permission(request, obj) or
|
||||||
inline.has_delete_permission(request, obj)):
|
inline.has_delete_permission(request, obj)):
|
||||||
continue
|
continue
|
||||||
if not inline_has_add_permission:
|
if not inline.has_add_permission(request, obj):
|
||||||
inline.max_num = 0
|
inline.max_num = 0
|
||||||
inline_instances.append(inline)
|
inline_instances.append(inline)
|
||||||
|
|
||||||
|
@ -1475,7 +1473,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
for inline, formset in zip(inline_instances, formsets):
|
for inline, formset in zip(inline_instances, formsets):
|
||||||
fieldsets = list(inline.get_fieldsets(request, obj))
|
fieldsets = list(inline.get_fieldsets(request, obj))
|
||||||
readonly = list(inline.get_readonly_fields(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_change_permission = inline.has_change_permission(request, obj)
|
||||||
has_delete_permission = inline.has_delete_permission(request, obj)
|
has_delete_permission = inline.has_delete_permission(request, obj)
|
||||||
has_view_permission = inline.has_view_permission(request, obj)
|
has_view_permission = inline.has_view_permission(request, obj)
|
||||||
|
@ -2011,11 +2009,6 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
js.append('collapse%s.js' % extra)
|
js.append('collapse%s.js' % extra)
|
||||||
return forms.Media(js=['admin/js/%s' % url for url in js])
|
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):
|
def get_extra(self, request, obj=None, **kwargs):
|
||||||
"""Hook for customizing the number of extra inline forms."""
|
"""Hook for customizing the number of extra inline forms."""
|
||||||
return self.extra
|
return self.extra
|
||||||
|
@ -2061,7 +2054,7 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
|
|
||||||
base_model_form = defaults['form']
|
base_model_form = defaults['form']
|
||||||
can_change = self.has_change_permission(request, obj) if request else True
|
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):
|
class DeleteProtectedModelForm(base_model_form):
|
||||||
|
|
||||||
|
@ -2126,8 +2119,7 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
queryset = queryset.none()
|
queryset = queryset.none()
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def has_add_permission(self, request, obj=None):
|
def has_add_permission(self, request, obj):
|
||||||
# RemovedInDjango31Warning: obj becomes a mandatory argument.
|
|
||||||
if self.opts.auto_created:
|
if self.opts.auto_created:
|
||||||
# We're checking the rights to an auto-created intermediate model,
|
# We're checking the rights to an auto-created intermediate model,
|
||||||
# which doesn't have its own individual permissions. The user needs
|
# which doesn't have its own individual permissions. The user needs
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
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
|
|
||||||
from .test_checks import CheckTestCase
|
|
||||||
|
|
||||||
|
|
||||||
class HasAddPermissionObjTests(CheckTestCase):
|
|
||||||
def test_model_admin_inherited_valid(self):
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertIsValid(BandAdmin, Band)
|
|
||||||
|
|
||||||
def test_model_admin_valid(self):
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
def has_add_permission(self, request):
|
|
||||||
return super().has_add_permission(request)
|
|
||||||
|
|
||||||
self.assertIsValid(BandAdmin, Band)
|
|
||||||
|
|
||||||
def test_inline_admin_inherited_valid(self):
|
|
||||||
class SongInlineAdmin(TabularInline):
|
|
||||||
model = Song
|
|
||||||
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
inlines = [SongInlineAdmin]
|
|
||||||
|
|
||||||
self.assertIsValid(BandAdmin, Band)
|
|
||||||
|
|
||||||
def test_inline_admin_valid(self):
|
|
||||||
class SongInlineAdmin(TabularInline):
|
|
||||||
model = Song
|
|
||||||
|
|
||||||
def has_add_permission(self, request, obj):
|
|
||||||
return super().has_add_permission(request, obj)
|
|
||||||
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
inlines = [SongInlineAdmin]
|
|
||||||
|
|
||||||
self.assertIsValid(BandAdmin, Band)
|
|
||||||
|
|
||||||
def test_inline_admin_warning(self):
|
|
||||||
class SongInlineAdmin(TabularInline):
|
|
||||||
model = Song
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
|
||||||
return super().has_add_permission(request)
|
|
||||||
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
inlines = [SongInlineAdmin]
|
|
||||||
|
|
||||||
msg = (
|
|
||||||
"Update SongInlineAdmin.has_add_permission() to accept a "
|
|
||||||
"positional `obj` argument."
|
|
||||||
)
|
|
||||||
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):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpTestData(cls):
|
|
||||||
cls.band = Band.objects.create(name='The Doors', bio='', sign_date=date(1965, 1, 1))
|
|
||||||
cls.song = Song.objects.create(name='test', band=cls.band)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.site = AdminSite()
|
|
||||||
self.request = MockRequest()
|
|
||||||
self.request.POST = {
|
|
||||||
'song_set-TOTAL_FORMS': 4,
|
|
||||||
'song_set-INITIAL_FORMS': 1,
|
|
||||||
}
|
|
||||||
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)
|
|
|
@ -734,10 +734,6 @@ class ModelAdminPermissionTests(SimpleTestCase):
|
||||||
def has_perm(self, perm):
|
def has_perm(self, perm):
|
||||||
return perm == 'modeladmin.add_band'
|
return perm == 'modeladmin.add_band'
|
||||||
|
|
||||||
class MockAddUserWithInline(MockUser):
|
|
||||||
def has_perm(self, perm):
|
|
||||||
return perm == 'modeladmin.add_concert'
|
|
||||||
|
|
||||||
class MockChangeUser(MockUser):
|
class MockChangeUser(MockUser):
|
||||||
def has_perm(self, perm):
|
def has_perm(self, perm):
|
||||||
return perm == 'modeladmin.change_band'
|
return perm == 'modeladmin.change_band'
|
||||||
|
@ -797,26 +793,6 @@ class ModelAdminPermissionTests(SimpleTestCase):
|
||||||
self.assertEqual(len(inline_instances), 1)
|
self.assertEqual(len(inline_instances), 1)
|
||||||
self.assertIsInstance(inline_instances[0], ConcertInline)
|
self.assertIsInstance(inline_instances[0], ConcertInline)
|
||||||
|
|
||||||
def test_inline_has_add_permission_without_obj(self):
|
|
||||||
# This test will be removed in Django 3.1 when `obj` becomes a required
|
|
||||||
# argument of has_add_permission() (#27991).
|
|
||||||
class ConcertInline(TabularInline):
|
|
||||||
model = Concert
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
|
||||||
return super().has_add_permission(request)
|
|
||||||
|
|
||||||
class BandAdmin(ModelAdmin):
|
|
||||||
inlines = [ConcertInline]
|
|
||||||
|
|
||||||
ma = BandAdmin(Band, AdminSite())
|
|
||||||
request = MockRequest()
|
|
||||||
request.user = self.MockAddUserWithInline()
|
|
||||||
band = Band(name='The Doors', bio='', sign_date=date(1965, 1, 1))
|
|
||||||
inline_instances = ma.get_inline_instances(request, band)
|
|
||||||
self.assertEqual(len(inline_instances), 1)
|
|
||||||
self.assertIsInstance(inline_instances[0], ConcertInline)
|
|
||||||
|
|
||||||
def test_has_change_permission(self):
|
def test_has_change_permission(self):
|
||||||
"""
|
"""
|
||||||
has_change_permission returns True for users who can edit objects and
|
has_change_permission returns True for users who can edit objects and
|
||||||
|
|
Loading…
Reference in New Issue