Skip to content

Commit 3061071

Browse files
committed
Adding related objects in the admin (via popup) respects user
permissions. Patch from SmileyChris. Fixed #1035. git-svn-id: http://code.djangoproject.com/svn/django/trunk@13708 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 92b91d6 commit 3061071

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

django/contrib/admin/options.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ def formfield_for_dbfield(self, db_field, **kwargs):
107107
# rendered output. formfield can be None if it came from a
108108
# OneToOneField with parent_link=True or a M2M intermediary.
109109
if formfield and db_field.name not in self.raw_id_fields:
110-
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
110+
related_modeladmin = self.admin_site._registry.get(
111+
db_field.rel.to)
112+
can_add_related = bool(related_modeladmin and
113+
related_modeladmin.has_add_permission(request))
114+
formfield.widget = widgets.RelatedFieldWidgetWrapper(
115+
formfield.widget, db_field.rel, self.admin_site,
116+
can_add_related=can_add_related)
111117

112118
return formfield
113119

django/contrib/admin/widgets.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,18 @@ class RelatedFieldWidgetWrapper(forms.Widget):
205205
This class is a wrapper to a given widget to add the add icon for the
206206
admin interface.
207207
"""
208-
def __init__(self, widget, rel, admin_site):
208+
def __init__(self, widget, rel, admin_site, can_add_related=None):
209209
self.is_hidden = widget.is_hidden
210210
self.needs_multipart_form = widget.needs_multipart_form
211211
self.attrs = widget.attrs
212212
self.choices = widget.choices
213213
self.widget = widget
214214
self.rel = rel
215+
# Backwards compatible check for whether a user can add related
216+
# objects.
217+
if can_add_related is None:
218+
can_add_related = rel_to in self.admin_site._registry
219+
self.can_add_related = can_add_related
215220
# so we can check if the related object is registered with this AdminSite
216221
self.admin_site = admin_site
217222

@@ -236,7 +241,7 @@ def render(self, name, value, *args, **kwargs):
236241
related_url = '%s%s/%s/add/' % info
237242
self.widget.choices = self.choices
238243
output = [self.widget.render(name, value, *args, **kwargs)]
239-
if rel_to in self.admin_site._registry: # If the related object has an admin interface:
244+
if self.can_add_related:
240245
# TODO: "id_" is hard-coded here. This should instead use the correct
241246
# API to determine the ID dynamically.
242247
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \

tests/regressiontests/admin_views/tests.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,28 @@ def testChangeView(self):
604604
'Plural error message not found in response to post with multiple errors.')
605605
self.client.get('/test_admin/admin/logout/')
606606

607+
def testConditionallyShowAddSectionLink(self):
608+
"""
609+
The foreign key widget should only show the "add related" button if the
610+
user has permission to add that related item.
611+
"""
612+
# Set up and log in user.
613+
url = '/test_admin/admin/admin_views/article/add/'
614+
add_link_text = ' class="add-another"'
615+
self.client.get('/test_admin/admin/')
616+
self.client.post('/test_admin/admin/', self.adduser_login)
617+
# The add user can't add sections yet, so they shouldn't see the "add
618+
# section" link.
619+
response = self.client.get(url)
620+
self.assertNotContains(response, add_link_text)
621+
# Allow the add user to add sections too. Now they can see the "add
622+
# section" link.
623+
add_user = User.objects.get(username='adduser')
624+
perm = get_perm(Section, Section._meta.get_add_permission())
625+
add_user.user_permissions.add(perm)
626+
response = self.client.get(url)
627+
self.assertContains(response, add_link_text)
628+
607629
def testCustomModelAdminTemplates(self):
608630
self.client.get('/test_admin/admin/')
609631
self.client.post('/test_admin/admin/', self.super_login)

0 commit comments

Comments
 (0)