Index: db/models/options.py =================================================================== --- db/models/options.py (revision 7737) +++ db/models/options.py (working copy) @@ -296,6 +296,9 @@ def get_delete_permission(self): return 'delete_%s' % self.object_name.lower() + def get_view_permission(self): + return 'view_%s' % self.object_name.lower() + def get_all_related_objects(self, local_only=False): try: self._related_objects_cache Index: contrib/admin/options.py =================================================================== --- contrib/admin/options.py (revision 7737) +++ contrib/admin/options.py (working copy) @@ -319,6 +319,17 @@ opts = self.opts return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()) + def has_view_permission(self, request, obj=None): + """ + Returns True if the given request has permission to view the given + Django model instance. + + If `obj` is None, this should return True if the given request has + permission to view *any* object of the given type. + """ + opts = self.opts + return request.user.has_perm(opts.app_label + '.' + opts.get_view_permission()) + def queryset(self, request): """ Returns a QuerySet of all model instances that can be edited by the @@ -557,7 +568,7 @@ # to determine whether a given object exists. obj = None - if not self.has_change_permission(request, obj): + if not (self.has_change_permission(request, obj) or (not request.POST and self.has_view_permission(request, obj))): raise PermissionDenied if obj is None: @@ -625,7 +636,7 @@ from django.contrib.admin.views.main import ChangeList, ERROR_FLAG opts = self.model._meta app_label = opts.app_label - if not self.has_change_permission(request, None): + if not (self.has_change_permission(request, None) or (not request.POST and self.has_view_permission(request, None))): raise PermissionDenied try: cl = ChangeList(request, self.model, self.list_display, self.list_display_links, self.list_filter, Index: contrib/admin/templatetags/admin_modify.py =================================================================== --- contrib/admin/templatetags/admin_modify.py (revision 7737) +++ contrib/admin/templatetags/admin_modify.py (working copy) @@ -16,6 +16,6 @@ 'show_save_and_add_another': context['has_add_permission'] and not is_popup and (not save_as or context['add']), 'show_save_and_continue': not is_popup and context['has_change_permission'], - 'show_save': True + 'show_save': change and context['has_change_permission'] or context['add'] and context['has_add_permission'] } submit_row = register.inclusion_tag('admin/submit_line.html', takes_context=True)(submit_row) Index: contrib/admin/templates/admin/index.html =================================================================== --- contrib/admin/templates/admin/index.html (revision 7737) +++ contrib/admin/templates/admin/index.html (working copy) @@ -19,7 +19,7 @@