It involves quite a hassle. I'll dump few ideas below.
First, let's show a model with a view
prermission in the admin:
from django.contrib import admin
from django.contrib.auth import get_permission_codename
from .models import MyModel
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def get_model_perms(self, request):
model_perms = super(MyModelAdmin, self).get_model_perms(request)
view_perm = self.has_view_permission(request)
model_perms.update({
'change': view_perm,
'view': view_perm,
})
def has_view_permission(self, request):
opts = self.opts
codename = get_permission_codename('view', opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
Note: I'm hooking in the changelist_view()
. I find it easier.
Secondly, let's add two views viewlist_view()
and view_view()
to the MyModelAdmin
:
def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
wrapper.model_admin = self
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(MyModelAdmin, self).get_urls()
urls = [
url(r'^viewlist/$', wrap(self.viewlist_view), name='%s_%s_viewlist' % info),
url(r'^(.+)/view/$', wrap(self.view_view), name='%s_%s_view' % info),
]
return urls + urlpatterns
@csrf_protect_m
def viewlist_view(self, request, extra_context=None):
opts = self.model._meta
app_label = opts.app_label
if not self.has_view_permission(request):
raise PermissionDenied
context = dict(
self.admin_site.each_context(request),
title='%s view list' % force_text(opts.verbose_name),
queryset=self.get_queryset(request),
)
context.update(extra_context or {})
return TemplateResponse(request, 'admin/view_list.html', context)
@csrf_protect_m
def view_view(self, request, object_id, extra_context=None):
opts = self.model._meta
app_label = opts.app_label
if not self.has_view_permission(request):
raise PermissionDenied
context = dict(
self.admin_site.each_context(request),
object_id=object_id,
)
return TemplateResponse(request, 'admin/view_view.html', context)
Thirdly, hook in the changelist_view()
:
@csrf_protect_m
def changelist_view(self, request, extra_context=None):
if not self.has_change_permission(request, None):
# or redirect
return self.viewlist_view(request, extra_context)
return super(MyModelAdmin, self).changelist_view(request, extra_context)
Fourthly, add two templates:
admin/view_list.html
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content-main">
<ul>
{% for obj in queryset %}
<li><a href="{{ obj.get_admin_view_url }}">{{ obj }}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}
admin/view_view.html
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content-main">
{{ object_id }}
</div>
{% endblock %}
Lastly, I've added a method get_admin_view_url()
to the MyModel
to get to the admin view:
from __future__ import unicode_literals
from django.db import models
from django.core.urlresolvers import reverse
class MyModel(models.Model):
name = models.CharField(max_length=255)
class Meta:
default_permissions = ('add', 'change', 'delete', 'view')
def __unicode__(self):
return self.name
def get_admin_view_url(self):
info = (self._meta.app_label, self._meta.model_name)
return reverse('admin:%s_%s_view' % info, args=(self.id,))
I hope this makes sense.