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
This commit is contained in:
parent
e2f9c11736
commit
b1b1da1eac
|
@ -270,6 +270,41 @@ class BaseModelAdmin(object):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
|
||||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
|
||||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class ModelAdmin(BaseModelAdmin):
|
|||
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 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||
# 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 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||
"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 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||
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'
|
||||
|
||||
|
|
|
@ -424,6 +424,7 @@ class GenericInlineModelAdmin(InlineModelAdmin):
|
|||
# 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 @@ class GenericInlineModelAdmin(InlineModelAdmin):
|
|||
"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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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 @@ class TestInline(TestCase):
|
|||
'<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 @@ class TestInlineAdminForm(TestCase):
|
|||
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"')
|
||||
|
|
|
@ -4,6 +4,18 @@ from django.contrib.admin.options import ModelAdmin
|
|||
|
||||
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 @@ class TestAdminOrdering(TestCase):
|
|||
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 @@ class TestAdminOrdering(TestCase):
|
|||
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 @@ class TestInlineModelAdminOrdering(TestCase):
|
|||
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 @@ class TestInlineModelAdminOrdering(TestCase):
|
|||
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)
|
||||
|
|
|
@ -216,6 +216,18 @@ class NoInlineDeletionTest(TestCase):
|
|||
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 @@ class GenericInlineModelAdminTest(TestCase):
|
|||
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 @@ class GenericInlineModelAdminTest(TestCase):
|
|||
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 @@ class GenericInlineModelAdminTest(TestCase):
|
|||
`ModelAdmin.exclude` or `GenericInlineModelAdmin.exclude` are defined.
|
||||
Refs #15907.
|
||||
"""
|
||||
|
||||
request = None
|
||||
|
||||
# First with `GenericInlineModelAdmin` -----------------
|
||||
|
||||
class MediaForm(ModelForm):
|
||||
|
|
|
@ -19,9 +19,15 @@ from models import (Band, Concert, ValidationTestModel,
|
|||
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 ModelAdminTests(TestCase):
|
|||
|
||||
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 ###########################################
|
||||
|
|
Loading…
Reference in New Issue