diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index a80509f801..6120182c87 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -749,7 +749,13 @@ class ModelAdmin(BaseModelAdmin): return HttpResponseRedirect("../add/") else: self.message_user(request, msg) - return HttpResponseRedirect("../") + # Figure out where to redirect. If the user has change permission, + # redirect to the change-list page for this object. Otherwise, + # redirect to the admin index. + if self.has_change_permission(request, None): + return HttpResponseRedirect('../') + else: + return HttpResponseRedirect('../../../') def response_action(self, request, queryset): """ diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index 6e0f9997fb..17d3dffe4d 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -232,6 +232,13 @@ class PersonAdmin(admin.ModelAdmin): return super(PersonAdmin, self).get_changelist_formset(request, formset=BasePersonModelFormSet, **kwargs) +class RowLevelChangePermissionModel(models.Model): + name = models.CharField(max_length=100, blank=True) + +class RowLevelChangePermissionModelAdmin(admin.ModelAdmin): + def has_change_permission(self, request, obj=None): + """ Only allow changing objects with even id number """ + return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0) class Persona(models.Model): """ @@ -698,6 +705,7 @@ admin.site.register(CyclicTwo) admin.site.register(WorkHour, WorkHourAdmin) admin.site.register(Reservation) admin.site.register(FoodDelivery, FoodDeliveryAdmin) +admin.site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index e93ec66e4d..be6c6bb05e 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -31,7 +31,7 @@ from models import (Article, BarAccount, CustomArticle, EmptyModel, Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, Category, Post, Plot, FunkyTag, WorkHour, Employee, Inquisition, - Actor, FoodDelivery) + Actor, FoodDelivery, RowLevelChangePermissionModel) class AdminViewBasicTest(TestCase): @@ -707,6 +707,42 @@ class AdminViewPermissionsTest(TestCase): 'Plural error message not found in response to post with multiple errors.') self.client.get('/test_admin/admin/logout/') + # Test redirection when using row-level change permissions. Refs #11513. + RowLevelChangePermissionModel.objects.create(name="odd id") + RowLevelChangePermissionModel.objects.create(name="even id") + for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]: + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', login_dict) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(request.status_code, 403) + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') + self.assertEqual(request.status_code, 403) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(request.status_code, 200) + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') + self.assertRedirects(request, '/test_admin/admin/') + self.client.get('/test_admin/admin/logout/') + for login_dict in [self.joepublic_login, self.no_username_login]: + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', login_dict) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + self.client.get('/test_admin/admin/logout/') + def testCustomModelAdminTemplates(self): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login)