Wednesday 10 July 2013

Create readonly permission for all models - Django

Create read only permission for all models - Django

Is it possible to create readonly /viewonly permission for all models in django. If admin provide read only permission to a particular user, an user can't edit record and can view only. So here is a very simple code for the same. Created an app and have the following features:
  • If user have a permission to view only, user cant edit record or in others word can only view.
  • Submit row will disappear, as an user dont have any benefits for - Save, Save & Continue, Save & Add buttons.
Follow these steps.
a. Create an app with the name of 'admin_hack' by this command -
"python manage.py startapp admin_hack" This will create following files.
admin_hack/
    __init__.py
    admin.py
    models.py
    tests.tpy
    views.py
b. Rename admin.py >> admin_hack.py
c. Write following code to admin_hack.py file
from django.contrib import admin
from django.db.models.signals import post_syncdb
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.util import flatten_fieldsets
from django.contrib.admin.templatetags.admin_modify import *
from django.contrib.admin.templatetags.admin_modify import submit_row as original_submit_row
def add_cannot_edit_record_permission(sender, **kwargs):
    """
    This syncdb hooks takes care of adding a view permission to all our
    content types.
    """
    for content_type in ContentType.objects.all():
        codename = "cannot_edit_record_for_%s" % content_type.model

        if not Permission.objects.filter(content_type=content_type, codename=codename):
            Permission.objects.create(
                content_type=content_type,
                codename=codename,
                name="Cannot edit record for %s" % content_type.name
            ) 
post_syncdb.connect(add_cannot_edit_record_permission)
class HackAdminModel(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        """Get readonly fields.

        :param request: HTTP request object.
        :param obj: An object.
        :return: Return readonly fields.
        """
        class_name = self.__class__.__name__.replace('Admin', '').lower()

        for permission in request.user.get_all_permissions():
            head, sep, tail = permission.partition('.')
            perm = "cannot_edit_record_for_%s" % (class_name)
            if str(perm) == str(tail):
                if request.user.has_perm(str(permission)) and not request.user.is_superuser:
                    if self.declared_fieldsets:
                        return flatten_fieldsets(self.declared_fieldsets)
                    else:
                        return list(set(
                            [field.name for field in self.opts.local_fields] +
                            [field.name for field in self.opts.local_many_to_many]
                        ))
        return self.readonly_fields

    @register.inclusion_tag('admin/submit_line.html', takes_context=True)
    def submit_row(context):
        """Sumbit row.

        :param context: Dictionary of required data.
        :return: Return update context.
        """
        ctx = original_submit_row(context)
        app_name, seprator, model_name = str(context.dicts[0]['opts']).partition('.')

        for permission in context['request'].user.get_all_permissions():
            head, sep, tail = permission.partition('.')
            perm = "cannot_edit_record_for_%s" % (model_name)
            if str(perm) == str(tail):
                if context['request'].user.has_perm(str(permission)) and \
                        not context['request'].user.is_superuser:
                    ctx.update({
                        'show_save_and_add_another': False,
                        'show_save_and_continue': False,
                        'show_save': False,
                    })
                return ctx
        return ctx
post_syncdb.connect(add_cannot_edit_record_permission) 
d. Add following code to models.py file
from admin_hack import *
e. Now go to your settings.py file and add this app to "INSTALLED_APPS"  like this
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django_tables2',
    'admin_hack',
    'pages',
 )
NOTE: Add this app i.e 'admin_hack' at the top of other created apps.
Now your app is ready to use.
Now question raise, how to use this app for all models. Using an example, going to use this app i.e admin_hack.
Here in the installed apps there is an app after admin_hack i.e 'pages'. I am gonna use this app for pages
Follow these steps:
a.  Go to the admin.py file of this app and import 'admin_hack' like this
# Hack Model
from admin_hack.admin_hack import HackAdminModel
b. Pass this 'HackAdminModel' to admin class like this
class PagesAdmin(HackAdminModel)
Before passing HackAdminModel the class PagesAdmin was like 
class PagesAdmin(admin.ModelAdmin) 
Now run following command to create permissions - "python manage.py syncdb" 
Testing of Admin Hack App
Now login to the admin site and view permissions. Permissions created for all models with the 
name as"Cannot edit record for '*' (* - signifies model name)
NOTE: This permission will work only for those models if you  pass 'HackAdminModel' 
to class ModelAdmin(admin.ModelAdmin) like this class ModelAdmin(HackAdminModel)

Code is on git hub and can download, here is the link: https://github.com/anupamshakya7/django-admin-hack

5 comments: