diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index f7b979b..42c1516 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -766,7 +766,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 60319ea..6ed76ea 100644
--- a/tests/regressiontests/admin_views/models.py
+++ b/tests/regressiontests/admin_views/models.py
@@ -133,6 +133,13 @@ class ArticleAdmin(admin.ModelAdmin):
         ).send()
         return super(ArticleAdmin, self).save_model(request, obj, form, change)
 
+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 CustomArticle(models.Model):
     content = models.TextField()
@@ -706,6 +713,7 @@ admin.site.register(CyclicOne)
 admin.site.register(CyclicTwo)
 admin.site.register(WorkHour, WorkHourAdmin)
 admin.site.register(Reservation)
+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 acbbbfc..ab23356 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -35,7 +35,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, Chapter, Book, Promo, WorkHour, Employee,
-    Question, Answer, Inquisition, Actor)
+    Question, Answer, Inquisition, Actor, RowLevelChangePermissionModel)
 
 
 class AdminViewBasicTest(TestCase):
@@ -792,6 +792,40 @@ 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.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.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 testConditionallyShowAddSectionLink(self):
         """
         The foreign key widget should only show the "add related" button if the
