Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #8060 - Added permissions-checking for admin inlines. Thanks p.…

…patruno for report and Stephan Jaensch for work on the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16934 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit b1b1da1eac93297503c04b8394fb98e38f552f5f 1 parent e2f9c11
@carljm carljm authored
View
149 django/contrib/admin/options.py
@@ -270,6 +270,41 @@ def lookup_allowed(self, lookup, value):
clean_lookup = LOOKUP_SEP.join(parts)
return clean_lookup in self.list_filter or clean_lookup == self.date_hierarchy
+ def has_add_permission(self, request):
+ """
+ Returns True if the given request has permission to add an object.
+ Can be overriden by the user in subclasses.
+ """
+ opts = self.opts
+ return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission())
+
+ def has_change_permission(self, request, obj=None):
+ """
+ Returns True if the given request has permission to change the given
+ Django model instance, the default implementation doesn't examine the
+ `obj` parameter.
+
+ Can be overriden by the user in subclasses. In such case it should
+ return True if the given request has permission to change the `obj`
+ model instance. If `obj` is None, this should return True if the given
+ request has permission to change *any* object of the given type.
+ """
+ opts = self.opts
+ return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission())
+
+ def has_delete_permission(self, request, obj=None):
+ """
+ Returns True if the given request has permission to change the given
+ Django model instance, the default implementation doesn't examine the
+ `obj` parameter.
+
+ Can be overriden by the user in subclasses. In such case it should
+ return True if the given request has permission to delete the `obj`
+ model instance. If `obj` is None, this should return True if the given
+ request has permission to delete *any* object of the given type.
+ """
+ opts = self.opts
+ return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission())
class ModelAdmin(BaseModelAdmin):
"Encapsulates all admin options and functionality for a given model."
@@ -307,10 +342,6 @@ def __init__(self, model, admin_site):
self.model = model
self.opts = model._meta
self.admin_site = admin_site
- self.inline_instances = []
- for inline_class in self.inlines:
- inline_instance = inline_class(self.model, self.admin_site)
- self.inline_instances.append(inline_instance)
if 'action_checkbox' not in self.list_display and self.actions is not None:
self.list_display = ['action_checkbox'] + list(self.list_display)
if not self.list_display_links:
@@ -320,6 +351,21 @@ def __init__(self, model, admin_site):
break
super(ModelAdmin, self).__init__()
+ def get_inline_instances(self, request):
+ inline_instances = []
+ for inline_class in self.inlines:
+ inline = inline_class(self.model, self.admin_site)
+ if request:
+ if not (inline.has_add_permission(request) or
+ inline.has_change_permission(request) or
+ inline.has_delete_permission(request)):
+ continue
+ if not inline.has_add_permission(request):
+ inline.max_num = 0
+ inline_instances.append(inline)
+
+ return inline_instances
+
def get_urls(self):
from django.conf.urls import patterns, url
@@ -369,42 +415,6 @@ def media(self):
js.extend(['getElementsBySelector.js', 'dom-drag.js' , 'admin/ordering.js'])
return forms.Media(js=[static('admin/js/%s' % url) for url in js])
- def has_add_permission(self, request):
- """
- Returns True if the given request has permission to add an object.
- Can be overriden by the user in subclasses.
- """
- opts = self.opts
- return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission())
-
- def has_change_permission(self, request, obj=None):
- """
- Returns True if the given request has permission to change the given
- Django model instance, the default implementation doesn't examine the
- `obj` parameter.
-
- Can be overriden by the user in subclasses. In such case it should
- return True if the given request has permission to change the `obj`
- model instance. If `obj` is None, this should return True if the given
- request has permission to change *any* object of the given type.
- """
- opts = self.opts
- return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission())
-
- def has_delete_permission(self, request, obj=None):
- """
- Returns True if the given request has permission to change the given
- Django model instance, the default implementation doesn't examine the
- `obj` parameter.
-
- Can be overriden by the user in subclasses. In such case it should
- return True if the given request has permission to delete the `obj`
- model instance. If `obj` is None, this should return True if the given
- request has permission to delete *any* object of the given type.
- """
- opts = self.opts
- return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission())
-
def get_model_perms(self, request):
"""
Returns a dict of all perms for this model. This dict has the keys
@@ -500,7 +510,7 @@ def get_changelist_formset(self, request, **kwargs):
fields=self.list_editable, **defaults)
def get_formsets(self, request, obj=None):
- for inline in self.inline_instances:
+ for inline in self.get_inline_instances(request):
yield inline.get_formset(request, obj)
def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
@@ -914,6 +924,7 @@ def add_view(self, request, form_url='', extra_context=None):
ModelForm = self.get_form(request)
formsets = []
+ inline_instances = self.get_inline_instances(request)
if request.method == 'POST':
form = ModelForm(request.POST, request.FILES)
if form.is_valid():
@@ -923,7 +934,7 @@ def add_view(self, request, form_url='', extra_context=None):
form_validated = False
new_object = self.model()
prefixes = {}
- for FormSet, inline in zip(self.get_formsets(request), self.inline_instances):
+ for FormSet, inline in zip(self.get_formsets(request), inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1 or not prefix:
@@ -951,8 +962,7 @@ def add_view(self, request, form_url='', extra_context=None):
initial[k] = initial[k].split(",")
form = ModelForm(initial=initial)
prefixes = {}
- for FormSet, inline in zip(self.get_formsets(request),
- self.inline_instances):
+ for FormSet, inline in zip(self.get_formsets(request), inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1 or not prefix:
@@ -968,7 +978,7 @@ def add_view(self, request, form_url='', extra_context=None):
media = self.media + adminForm.media
inline_admin_formsets = []
- for inline, formset in zip(self.inline_instances, formsets):
+ for inline, formset in zip(inline_instances, formsets):
fieldsets = list(inline.get_fieldsets(request))
readonly = list(inline.get_readonly_fields(request))
prepopulated = dict(inline.get_prepopulated_fields(request))
@@ -1012,6 +1022,7 @@ def change_view(self, request, object_id, extra_context=None):
ModelForm = self.get_form(request, obj)
formsets = []
+ inline_instances = self.get_inline_instances(request)
if request.method == 'POST':
form = ModelForm(request.POST, request.FILES, instance=obj)
if form.is_valid():
@@ -1021,8 +1032,7 @@ def change_view(self, request, object_id, extra_context=None):
form_validated = False
new_object = obj
prefixes = {}
- for FormSet, inline in zip(self.get_formsets(request, new_object),
- self.inline_instances):
+ for FormSet, inline in zip(self.get_formsets(request, new_object), inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1 or not prefix:
@@ -1043,7 +1053,7 @@ def change_view(self, request, object_id, extra_context=None):
else:
form = ModelForm(instance=obj)
prefixes = {}
- for FormSet, inline in zip(self.get_formsets(request, obj), self.inline_instances):
+ for FormSet, inline in zip(self.get_formsets(request, obj), inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1 or not prefix:
@@ -1059,7 +1069,7 @@ def change_view(self, request, object_id, extra_context=None):
media = self.media + adminForm.media
inline_admin_formsets = []
- for inline, formset in zip(self.inline_instances, formsets):
+ for inline, formset in zip(inline_instances, formsets):
fieldsets = list(inline.get_fieldsets(request, obj))
readonly = list(inline.get_readonly_fields(request, obj))
prepopulated = dict(inline.get_prepopulated_fields(request, obj))
@@ -1377,6 +1387,7 @@ def get_formset(self, request, obj=None, **kwargs):
# if exclude is an empty list we use None, since that's the actual
# default
exclude = exclude or None
+ can_delete = self.can_delete and self.has_delete_permission(request, obj)
defaults = {
"form": self.form,
"formset": self.formset,
@@ -1386,7 +1397,7 @@ def get_formset(self, request, obj=None, **kwargs):
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
"extra": self.extra,
"max_num": self.max_num,
- "can_delete": self.can_delete,
+ "can_delete": can_delete,
}
defaults.update(kwargs)
return inlineformset_factory(self.parent_model, self.model, **defaults)
@@ -1398,6 +1409,44 @@ def get_fieldsets(self, request, obj=None):
fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj))
return [(None, {'fields': fields})]
+ def queryset(self, request):
+ queryset = super(InlineModelAdmin, self).queryset(request)
+ if not self.has_change_permission(request):
+ queryset = queryset.none()
+ return queryset
+
+ def has_add_permission(self, request):
+ if self.opts.auto_created:
+ # We're checking the rights to an auto-created intermediate model,
+ # which doesn't have its own individual permissions. The user needs
+ # to have the change permission for the related model in order to
+ # be able to do anything with the intermediate model.
+ return self.has_change_permission(request)
+ return request.user.has_perm(
+ self.opts.app_label + '.' + self.opts.get_add_permission())
+
+ def has_change_permission(self, request, obj=None):
+ opts = self.opts
+ if opts.auto_created:
+ # The model was auto-created as intermediary for a
+ # ManyToMany-relationship, find the target model
+ for field in opts.fields:
+ if field.rel and field.rel.to != self.parent_model:
+ opts = field.rel.to._meta
+ break
+ return request.user.has_perm(
+ opts.app_label + '.' + opts.get_change_permission())
+
+ def has_delete_permission(self, request, obj=None):
+ if self.opts.auto_created:
+ # We're checking the rights to an auto-created intermediate model,
+ # which doesn't have its own individual permissions. The user needs
+ # to have the change permission for the related model in order to
+ # be able to do anything with the intermediate model.
+ return self.has_change_permission(request, obj)
+ return request.user.has_perm(
+ self.opts.app_label + '.' + self.opts.get_delete_permission())
+
class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html'
View
3  django/contrib/contenttypes/generic.py
@@ -424,6 +424,7 @@ def get_formset(self, request, obj=None, **kwargs):
# GenericInlineModelAdmin doesn't define its own.
exclude.extend(self.form._meta.exclude)
exclude = exclude or None
+ can_delete = self.can_delete and self.has_delete_permission(request, obj)
defaults = {
"ct_field": self.ct_field,
"fk_field": self.ct_fk_field,
@@ -431,7 +432,7 @@ def get_formset(self, request, obj=None, **kwargs):
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
"formset": self.formset,
"extra": self.extra,
- "can_delete": self.can_delete,
+ "can_delete": can_delete,
"can_order": False,
"fields": fields,
"max_num": self.max_num,
View
8 docs/ref/contrib/admin/index.txt
@@ -1391,11 +1391,17 @@ adds some of its own (the shared features are actually defined in the
- :attr:`~ModelAdmin.ordering`
- :meth:`~ModelAdmin.queryset`
+.. versionadded:: 1.4
+
+- :meth:`~ModelAdmin.has_add_permission`
+- :meth:`~ModelAdmin.has_change_permission`
+- :meth:`~ModelAdmin.has_delete_permission`
+
The ``InlineModelAdmin`` class adds:
.. attribute:: InlineModelAdmin.model
- The model in which the inline is using. This is required.
+ The model which the inline is using. This is required.
.. attribute:: InlineModelAdmin.fk_name
View
9 docs/releases/1.4.txt
@@ -128,6 +128,15 @@ A new :meth:`~django.contrib.admin.ModelAdmin.save_related` hook was added to
:mod:`~django.contrib.admin.ModelAdmin` to ease the customization of how
related objects are saved in the admin.
+Admin inlines respect user permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Admin inlines will now only allow those actions for which the user has
+permission. For ``ManyToMany`` relationships with an auto-created intermediate
+model (which does not have its own permissions), the change permission for the
+related model determines if the user has the permission to add, change or
+delete relationships.
+
Tools for cryptographic signing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
View
183 tests/regressiontests/admin_inlines/tests.py
@@ -1,11 +1,12 @@
from django.contrib.admin.helpers import InlineAdminForm
+from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
# local test models
from models import (Holder, Inner, Holder2, Inner2, Holder3,
Inner3, Person, OutfitItem, Fashionista, Teacher, Parent, Child,
- CapoFamiglia, Consigliere, SottoCapo)
+ Author, Book)
from admin import InnerInline
@@ -141,7 +142,6 @@ def test_non_related_name_inline(self):
'<input id="id_-2-0-name" type="text" class="vTextField" '
'name="-2-0-name" maxlength="100" />')
-
class TestInlineMedia(TestCase):
urls = "regressiontests.admin_inlines.urls"
fixtures = ['admin-views-users.xml']
@@ -196,3 +196,182 @@ def test_immutable_content_type(self):
iaf = InlineAdminForm(None, None, {}, {}, joe)
parent_ct = ContentType.objects.get_for_model(Parent)
self.assertEqual(iaf.original.content_type, parent_ct)
+
+class TestInlinePermissions(TestCase):
+ """
+ Make sure the admin respects permissions for objects that are edited
+ inline. Refs #8060.
+
+ """
+ urls = "regressiontests.admin_inlines.urls"
+
+ def setUp(self):
+ self.user = User(username='admin')
+ self.user.is_staff = True
+ self.user.is_active = True
+ self.user.set_password('secret')
+ self.user.save()
+
+ self.author_ct = ContentType.objects.get_for_model(Author)
+ self.holder_ct = ContentType.objects.get_for_model(Holder2)
+ self.book_ct = ContentType.objects.get_for_model(Book)
+ self.inner_ct = ContentType.objects.get_for_model(Inner2)
+
+ # User always has permissions to add and change Authors, and Holders,
+ # the main (parent) models of the inlines. Permissions on the inlines
+ # vary per test.
+ permission = Permission.objects.get(codename='add_author', content_type=self.author_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='change_author', content_type=self.author_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='add_holder2', content_type=self.holder_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='change_holder2', content_type=self.holder_ct)
+ self.user.user_permissions.add(permission)
+
+ author = Author.objects.create(pk=1, name=u'The Author')
+ author.books.create(name=u'The inline Book')
+ self.author_change_url = '/admin/admin_inlines/author/%i/' % author.id
+
+ holder = Holder2.objects.create(dummy=13)
+ Inner2.objects.create(dummy=42, holder=holder)
+ self.holder_change_url = '/admin/admin_inlines/holder2/%i/' % holder.id
+
+ self.assertEqual(
+ self.client.login(username='admin', password='secret'),
+ True)
+
+ def tearDown(self):
+ self.client.logout()
+
+ def test_inline_add_m2m_noperm(self):
+ response = self.client.get('/admin/admin_inlines/author/add/')
+ # No change permission on books, so no inline
+ self.assertNotContains(response, '<h2>Author-book relationships</h2>')
+ self.assertNotContains(response, 'Add another Author-Book Relationship')
+ self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
+
+ def test_inline_add_fk_noperm(self):
+ response = self.client.get('/admin/admin_inlines/holder2/add/')
+ # No permissions on Inner2s, so no inline
+ self.assertNotContains(response, '<h2>Inner2s</h2>')
+ self.assertNotContains(response, 'Add another Inner2')
+ self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"')
+
+ def test_inline_change_m2m_noperm(self):
+ response = self.client.get(self.author_change_url)
+ # No change permission on books, so no inline
+ self.assertNotContains(response, '<h2>Author-book relationships</h2>')
+ self.assertNotContains(response, 'Add another Author-Book Relationship')
+ self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
+
+ def test_inline_change_fk_noperm(self):
+ response = self.client.get(self.holder_change_url)
+ # No permissions on Inner2s, so no inline
+ self.assertNotContains(response, '<h2>Inner2s</h2>')
+ self.assertNotContains(response, 'Add another Inner2')
+ self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"')
+
+ def test_inline_add_m2m_add_perm(self):
+ permission = Permission.objects.get(codename='add_book', content_type=self.book_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get('/admin/admin_inlines/author/add/')
+ # No change permission on Books, so no inline
+ self.assertNotContains(response, '<h2>Author-book relationships</h2>')
+ self.assertNotContains(response, 'Add another Author-Book Relationship')
+ self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
+
+ def test_inline_add_fk_add_perm(self):
+ permission = Permission.objects.get(codename='add_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get('/admin/admin_inlines/holder2/add/')
+ # Add permission on inner2s, so we get the inline
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ self.assertContains(response, 'Add another Inner2')
+ self.assertContains(response, 'value="3" id="id_inner2_set-TOTAL_FORMS"')
+
+ def test_inline_change_m2m_add_perm(self):
+ permission = Permission.objects.get(codename='add_book', content_type=self.book_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.author_change_url)
+ # No change permission on books, so no inline
+ self.assertNotContains(response, '<h2>Author-book relationships</h2>')
+ self.assertNotContains(response, 'Add another Author-Book Relationship')
+ self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
+ self.assertNotContains(response, 'id="id_Author_books-0-DELETE"')
+
+ def test_inline_change_m2m_change_perm(self):
+ permission = Permission.objects.get(codename='change_book', content_type=self.book_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.author_change_url)
+ # We have change perm on books, so we can add/change/delete inlines
+ self.assertContains(response, '<h2>Author-book relationships</h2>')
+ self.assertContains(response, 'Add another Author-Book Relationship')
+ self.assertContains(response, 'value="4" id="id_Author_books-TOTAL_FORMS"')
+ self.assertContains(response, '<input type="hidden" name="Author_books-0-id" value="1"')
+ self.assertContains(response, 'id="id_Author_books-0-DELETE"')
+
+ def test_inline_change_fk_add_perm(self):
+ permission = Permission.objects.get(codename='add_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.holder_change_url)
+ # Add permission on inner2s, so we can add but not modify existing
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ self.assertContains(response, 'Add another Inner2')
+ # 3 extra forms only, not the existing instance form
+ self.assertContains(response, 'value="3" id="id_inner2_set-TOTAL_FORMS"')
+ self.assertNotContains(response, '<input type="hidden" name="inner2_set-0-id" value="1"')
+
+ def test_inline_change_fk_change_perm(self):
+ permission = Permission.objects.get(codename='change_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.holder_change_url)
+ # Change permission on inner2s, so we can change existing but not add new
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ # Just the one form for existing instances
+ self.assertContains(response, 'value="1" id="id_inner2_set-TOTAL_FORMS"')
+ self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="1"')
+ # max-num 0 means we can't add new ones
+ self.assertContains(response, 'value="0" id="id_inner2_set-MAX_NUM_FORMS"')
+
+ def test_inline_change_fk_add_change_perm(self):
+ permission = Permission.objects.get(codename='add_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='change_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.holder_change_url)
+ # Add/change perm, so we can add new and change existing
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ # One form for existing instance and three extra for new
+ self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
+ self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="1"')
+
+
+ def test_inline_change_fk_change_del_perm(self):
+ permission = Permission.objects.get(codename='change_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='delete_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.holder_change_url)
+ # Change/delete perm on inner2s, so we can change/delete existing
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ # One form for existing instance only, no new
+ self.assertContains(response, 'value="1" id="id_inner2_set-TOTAL_FORMS"')
+ self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="1"')
+ self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
+
+
+ def test_inline_change_fk_all_perms(self):
+ permission = Permission.objects.get(codename='add_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='change_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ permission = Permission.objects.get(codename='delete_inner2', content_type=self.inner_ct)
+ self.user.user_permissions.add(permission)
+ response = self.client.get(self.holder_change_url)
+ # All perms on inner2s, so we can add/change/delete
+ self.assertContains(response, '<h2>Inner2s</h2>')
+ # One form for existing instance only, three for new
+ self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
+ self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="1"')
+ self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
View
22 tests/regressiontests/admin_ordering/tests.py
@@ -4,6 +4,18 @@
from models import Band, Song, SongInlineDefaultOrdering, SongInlineNewOrdering, DynOrderingBandAdmin
+
+class MockRequest(object):
+ pass
+
+class MockSuperUser(object):
+ def has_perm(self, perm):
+ return True
+
+request = MockRequest()
+request.user = MockSuperUser()
+
+
class TestAdminOrdering(TestCase):
"""
Let's make sure that ModelAdmin.queryset uses the ordering we define in
@@ -26,7 +38,7 @@ def test_default_ordering(self):
class.
"""
ma = ModelAdmin(Band, None)
- names = [b.name for b in ma.queryset(None)]
+ names = [b.name for b in ma.queryset(request)]
self.assertEqual([u'Aerosmith', u'Radiohead', u'Van Halen'], names)
def test_specified_ordering(self):
@@ -37,7 +49,7 @@ def test_specified_ordering(self):
class BandAdmin(ModelAdmin):
ordering = ('rank',) # default ordering is ('name',)
ma = BandAdmin(Band, None)
- names = [b.name for b in ma.queryset(None)]
+ names = [b.name for b in ma.queryset(request)]
self.assertEqual([u'Radiohead', u'Van Halen', u'Aerosmith'], names)
def test_dynamic_ordering(self):
@@ -79,7 +91,7 @@ def test_default_ordering(self):
class.
"""
inline = SongInlineDefaultOrdering(self.b, None)
- names = [s.name for s in inline.queryset(None)]
+ names = [s.name for s in inline.queryset(request)]
self.assertEqual([u'Dude (Looks Like a Lady)', u'Jaded', u'Pink'], names)
def test_specified_ordering(self):
@@ -87,5 +99,5 @@ def test_specified_ordering(self):
Let's check with ordering set to something different than the default.
"""
inline = SongInlineNewOrdering(self.b, None)
- names = [s.name for s in inline.queryset(None)]
- self.assertEqual([u'Jaded', u'Pink', u'Dude (Looks Like a Lady)'], names)
+ names = [s.name for s in inline.queryset(request)]
+ self.assertEqual([u'Jaded', u'Pink', u'Dude (Looks Like a Lady)'], names)
View
22 tests/regressiontests/generic_inline_admin/tests.py
@@ -216,6 +216,18 @@ def test_no_deletion(self):
formset = inline.get_formset(fake_request)
self.assertFalse(formset.can_delete)
+
+class MockRequest(object):
+ pass
+
+class MockSuperUser(object):
+ def has_perm(self, perm):
+ return True
+
+request = MockRequest()
+request.user = MockSuperUser()
+
+
class GenericInlineModelAdminTest(TestCase):
urls = "regressiontests.generic_inline_admin.urls"
@@ -226,12 +238,12 @@ def test_get_formset_kwargs(self):
media_inline = MediaInline(Media, AdminSite())
# Create a formset with default arguments
- formset = media_inline.get_formset(None)
+ formset = media_inline.get_formset(request)
self.assertEqual(formset.max_num, None)
self.assertEqual(formset.can_order, False)
# Create a formset with custom keyword arguments
- formset = media_inline.get_formset(None, max_num=100, can_order=True)
+ formset = media_inline.get_formset(request, max_num=100, can_order=True)
self.assertEqual(formset.max_num, 100)
self.assertEqual(formset.can_order, True)
@@ -241,9 +253,6 @@ def test_custom_form_meta_exclude_with_readonly(self):
used in conjunction with `GenericInlineModelAdmin.readonly_fields`
and when no `ModelAdmin.exclude` is defined.
"""
-
- request = None
-
class MediaForm(ModelForm):
class Meta:
@@ -272,9 +281,6 @@ def test_custom_form_meta_exclude(self):
`ModelAdmin.exclude` or `GenericInlineModelAdmin.exclude` are defined.
Refs #15907.
"""
-
- request = None
-
# First with `GenericInlineModelAdmin` -----------------
class MediaForm(ModelForm):
View
17 tests/regressiontests/modeladmin/tests.py
@@ -19,9 +19,15 @@
ValidationTestInlineModel)
-# None of the following tests really depend on the content of the request,
-# so we'll just pass in None.
-request = None
+class MockRequest(object):
+ pass
+
+class MockSuperUser(object):
+ def has_perm(self, perm):
+ return True
+
+request = MockRequest()
+request.user = MockSuperUser()
class ModelAdminTests(TestCase):
@@ -357,9 +363,10 @@ class BandAdmin(ModelAdmin):
concert = Concert.objects.create(main_band=self.band, opening_band=self.band, day=1)
ma = BandAdmin(Band, self.site)
- fieldsets = list(ma.inline_instances[0].get_fieldsets(request))
+ inline_instances = ma.get_inline_instances(request)
+ fieldsets = list(inline_instances[0].get_fieldsets(request))
self.assertEqual(fieldsets[0][1]['fields'], ['main_band', 'opening_band', 'day', 'transport'])
- fieldsets = list(ma.inline_instances[0].get_fieldsets(request, ma.inline_instances[0].model))
+ fieldsets = list(inline_instances[0].get_fieldsets(request, inline_instances[0].model))
self.assertEqual(fieldsets[0][1]['fields'], ['day'])
# radio_fields behavior ###########################################
Please sign in to comment.
Something went wrong with that request. Please try again.