7

I have an active Django project where the admin panel is used by the customer support team. Django lacks a view permission because of which I have to assign the change permission to the customer support team which is slightly dangerous. I have some models for which the customer support team needs just the view access and not the change access because of security issues. Why is the view permision missing in Django? Any workaround to this?

anon
  • 1,069
  • 1
  • 12
  • 17
  • Do you mean you want to make information in the admin read-only? – markwalker_ Oct 19 '15 at 03:50
  • @marksweb : Yes, but only for a specific group of users. I don't to make it read only globally across all the groups. – anon Oct 19 '15 at 07:08
  • 3
    Nobody has answered the question why is the `view` permission missing. – mehmet Oct 27 '16 at 14:43
  • My suspect is that you use custom managers and the objects that are not viewable gets filtered out in the first place so there is no need to check view permission. But I am not sure if this will work in all scenarios, e.g. related models. – mehmet Oct 27 '16 at 14:47
  • 1
    It's being added in 2.1: https://code.djangoproject.com/ticket/8936 – Nick Sweeting Jun 18 '18 at 21:12

2 Answers2

6

Here's a workaround.

Models

Simply create models with view permission by inheriting them from mixin:

class ViewPermissionsMixin(models.Model):
    """
        Mixin adds view permission to model.
    """
    class Meta:
        abstract=True
        default_permissions = ('add', 'change', 'delete', 'view')

Example model:

class ExampleModel(ViewPermissionsMixin):
    name = models.CharField(max_length=255)

    class Meta(ViewPermissionsMixin.Meta):
        abstract = False

This will add view permission that can be assigned to certain user/group. But such permission is useless without proper admin modification.

Admins

Here is mixin for your admins:

class AdminViewMixin(admin.ModelAdmin):

    def has_perm(self,user,permission):
        """
            Usefull shortcut for `user.has_perm()`
        """
        if user.has_perm("%s.%s_%s" % (self.model._meta.app_label,permission,self.model.__name__.lower(),)):
            return True
        return False

    def has_module_permission(self, request): # Django 1.8
        pass

    def has_change_permission(self, request, obj=None):
        """
            Necessary permission check to let Django show change_form for `view` permissions
        """
        if request.user.is_superuser:
            return True
        elif self.has_perm(request.user,'change'):
            return True
        elif self.has_perm(request.user,'view'):
            return True
        return super(AdminMixin, self).has_change_permission(request, obj)

    def get_readonly_fields(self, request, obj=None):
        """
            Turn each model field into read-only for `viewers`
        """
        all_model_fields = []
        for field in self.model._meta.fields:
            # TODO in Django 1.8 use ModelAdmin.get_fields()
            if not field.auto_created \
                and (not hasattr(field,'auto_now_add') or not field.auto_now_add) \
                and (not hasattr(field,'auto_now') or not field.auto_now) \
                :
                all_model_fields.append(field.name)
        if request.user.is_superuser:
            return self.readonly_fields
        elif self.has_perm(request.user,'change'):
            return self.readonly_fields
        elif self.has_perm(request.user,'view'):
            return all_model_fields
        return self.readonly_fields

    def change_view(self, request, object_id, extra_context=None):
        """
            Disable buttons for `viewers` in `change_view`
        """
        if request.user.is_superuser:
            pass
        elif self.has_perm(request.user,'change'):
            pass
        elif self.has_perm(request.user,'view'):
            extra_context = extra_context or {}
            extra_context['hide_save_buttons'] = True
        return super(AdminViewMixin, self).change_view(request, object_id, extra_context=extra_context)

Example admin:

@admin.register(models.ExampleModel)
class ExampleAdmin(AdminViewMixin):
    list_display = ('name',)
    pass

Finally just assign view permission for specific models to any user or group in your Django Admin.

radyz
  • 1,664
  • 1
  • 17
  • 26
pista329
  • 691
  • 8
  • 14
  • How does it compare with django-admin-view-permission [https://github.com/ctxis/django-admin-view-permission] ? – Don Sep 28 '17 at 10:19
2

I think this should work:

1.Add "view" permission, see https://stackoverflow.com/a/23411901/1266258

2.Customize "change" permission:

class FooAdmin(ModelAdmin):
    def has_change_permission(self, request, obj=None):
        # user can view the change list
        if not obj and request.user.has_perm('myapp.view_foo'):
            return True
        # user can view the change form and change the obj
        return request.user.has_perm('myapp.change_foo')
Community
  • 1
  • 1
JimmyYe
  • 844
  • 6
  • 6