Skip to content

Commit

Permalink
Adding related objects in the admin (via popup) respects user
Browse files Browse the repository at this point in the history
permissions. Patch from SmileyChris. Fixed #1035.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13708 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
malcolmt committed Sep 10, 2010
1 parent 92b91d6 commit 3061071
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
8 changes: 7 additions & 1 deletion django/contrib/admin/options.py
Expand Up @@ -107,7 +107,13 @@ def formfield_for_dbfield(self, db_field, **kwargs):
# rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields:
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
related_modeladmin = self.admin_site._registry.get(
db_field.rel.to)
can_add_related = bool(related_modeladmin and
related_modeladmin.has_add_permission(request))
formfield.widget = widgets.RelatedFieldWidgetWrapper(
formfield.widget, db_field.rel, self.admin_site,
can_add_related=can_add_related)

return formfield

Expand Down
9 changes: 7 additions & 2 deletions django/contrib/admin/widgets.py
Expand Up @@ -205,13 +205,18 @@ class RelatedFieldWidgetWrapper(forms.Widget):
This class is a wrapper to a given widget to add the add icon for the
admin interface.
"""
def __init__(self, widget, rel, admin_site):
def __init__(self, widget, rel, admin_site, can_add_related=None):
self.is_hidden = widget.is_hidden
self.needs_multipart_form = widget.needs_multipart_form
self.attrs = widget.attrs
self.choices = widget.choices
self.widget = widget
self.rel = rel
# Backwards compatible check for whether a user can add related
# objects.
if can_add_related is None:
can_add_related = rel_to in self.admin_site._registry
self.can_add_related = can_add_related
# so we can check if the related object is registered with this AdminSite
self.admin_site = admin_site

Expand All @@ -236,7 +241,7 @@ def render(self, name, value, *args, **kwargs):
related_url = '%s%s/%s/add/' % info
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
if rel_to in self.admin_site._registry: # If the related object has an admin interface:
if self.can_add_related:
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
Expand Down
22 changes: 22 additions & 0 deletions tests/regressiontests/admin_views/tests.py
Expand Up @@ -604,6 +604,28 @@ def testChangeView(self):
'Plural error message not found in response to post with multiple errors.')
self.client.get('/test_admin/admin/logout/')

def testConditionallyShowAddSectionLink(self):
"""
The foreign key widget should only show the "add related" button if the
user has permission to add that related item.
"""
# Set up and log in user.
url = '/test_admin/admin/admin_views/article/add/'
add_link_text = ' class="add-another"'
self.client.get('/test_admin/admin/')
self.client.post('/test_admin/admin/', self.adduser_login)
# The add user can't add sections yet, so they shouldn't see the "add
# section" link.
response = self.client.get(url)
self.assertNotContains(response, add_link_text)
# Allow the add user to add sections too. Now they can see the "add
# section" link.
add_user = User.objects.get(username='adduser')
perm = get_perm(Section, Section._meta.get_add_permission())
add_user.user_permissions.add(perm)
response = self.client.get(url)
self.assertContains(response, add_link_text)

def testCustomModelAdminTemplates(self):
self.client.get('/test_admin/admin/')
self.client.post('/test_admin/admin/', self.super_login)
Expand Down

0 comments on commit 3061071

Please sign in to comment.