django guardian is used to implement row level permission control of django admin

  • 2021-01-18 06:30:45
  • OfStack

django framework to do some backstage management of web page is simply too convenient, django comes with module level permissions system, used to do some internal system is very suitable, can greatly reduce the amount of development. However, the built-in django permission system does not support row-level permission control. If you want to achieve row-level permission control, you need to rely on the third party's app development, or you have to write a new one.

Requirements describe

Some systems developed by our project team usually use the mysql database to store some configuration, but if we manually modify the mysql data every time there is a configuration change, it will be quite troublesome, but also more prone to error. django-admin can automatically generate pages based on the defined model, as well as provide permission management, so we will put some system-specific configuration in django. But now, with the increasing requirements of the system, the system is not only used by our project team, but also open to the use of colleagues in other project teams, so some more fine-grained permission requirements have been generated. Therefore, we need to support row-level permission control on existing systems.

The solution

Of course, you can write a set of authority system, but the cost that you write is higher, and what you write is not better. So I just look on the Internet first 1 some ready-made solutions, https: / / djangopackages org/grids/g/perms/the link lists the existing 1 some third party rights system solutions. According to this page, django-guardian is the most popular third party permissions system, and supports row level permissions, as well as being integrated into django-admin, so I chose django-guardian.

Key steps

Install the configuration django-guardian

django-guardian is a simple setup, just follow the documentation provided by her project to install, after the installation will create two permissions related tables in the database.

Integrate django-guardian into django-admin

First put admin. py files inside need row-level permissions class, inherited from the original admin. ModelAdmin, to inherit GuardedModelAdmin, open the page a data line at this moment, in the top right-hand corner of the page displayed next to the history of editing object permissions button, click this button to enter the corresponding page specific privileges can edit the data.

This is because GuardedModelAdmin still has a lot of things to do for us. We still need to rewrite some functions to realize the display of admin background page. See the code comments below for more information.


from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm, get_users_with_perms, \
  get_groups_with_perms
  
#  Need to change before 
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(admin.ModelAdmin):
  pass

#  The modified 
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(GuardedModelAdmin):
  # app This function determines whether to display on the main page 
  def has_module_permission(self, request):
    if super().has_module_permission(request):
      return True
    return self.get_model_objs(request).exists()

  #  This function controls which data is displayed and which is not displayed when displaying a list of data 
  def get_queryset(self, request):
    if request.user.is_superuser:
      return super().get_queryset(request)

    data = self.get_model_objs(request)
    return data
    
  #  Internally used to get rows of data that a user has permission to access 
  def get_model_objs(self, request, action=None, klass=None):
    opts = self.opts
    actions = [action] if action else ['view', 'change', 'delete']
    klass = klass if klass else opts.model
    model_name = klass._meta.model_name
    return get_objects_for_user(user=request.user, perms=[f'{perm}_{model_name}' for perm in actions],
                  klass=klass, any_perm=True)

  #  Use to determine whether a user has permission for a data row 
  def has_perm(self, request, obj, action):
    opts = self.opts
    codename = f'{action}_{opts.model_name}'
    if obj:
      return request.user.has_perm(f'{opts.app_label}.{codename}', obj)
    else:
      return self.get_model_objs(request, action).exists()

  #  Whether you have permission to view a data row 
  def has_view_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'view')

  #  Whether you have permission to modify a data row 
  def has_change_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'change')

  #  Whether you have permission to delete a data row 
  def has_delete_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'delete')

  #  The user should have all permissions for his new data rows 
  def save_model(self, request, obj, form, change):
    result = super().save_model(request, obj, form, change)
    if not request.user.is_superuser and not change:
      opts = self.opts
      actions = ['view', 'add', 'change', 'delete']
      [assign_perm(f'{opts.app_label}.{action}_{opts.model_name}', request.user, obj) for action in actions]
    return result

Through the above changes, the django - admin module can support row-level permissions, and can be shown in the right page in the background, of course, if there's a lot of module needs to support row-level permissions control, you can write the above these changes in a new class, and then other modules to support row-level permissions inherited from the module.

conclusion

It feels that the integration of django-guardian and django-admin is not very good. If developers do not know much about the django internal code, then using django-guardian to achieve line-level access control will be very troublesome, I think django-guardian can be integrated with django-admin out of the box effect, as django comes with access system 1.


Related articles: