Как избежать опечаток в строках разрешений django

#django #django-permissions

#django #django-разрешения

Вопрос:

Согласно документам, пользовательские разрешения могут быть созданы и использованы следующим образом:

 class Task(models.Model):
    ...
    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
        )
  

Использование разрешения:

 user.has_perm('app.view_task')
  

Источник: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#custom-permissions

Если в строке разрешений есть опечатка. Например: Я использую user.has_perm('app.view_tasks') , тогда эта опечатка не будет замечена.

Цель 1

Я хотел бы получить исключение или предупреждение, если не используются существующие разрешения.

Цель 2

Чтобы избежать опечаток в первую очередь, я хотел бы иметь константы: user.PERM_APP_VIEW_TASKS или что-то вроде этого.

Комментарии:

1. Итак, в чем собственно вопрос? Для цели 1 вы можете изменить has_perm метод для пользователя, чтобы он проверял, существует ли разрешение в БД, Permission.objects.get(...) а для цели 2 вы можете создавать переменные класса

2. @SardorbekImomaliev вопрос в том, «как решить это самым питоническим способом?».

Ответ №1:

Цель 1

Переопределите has_perm метод ModelBackend класса из моего backends.py файла:

 import logging
from difflib import get_close_matches
from django.conf import settings
from django.contrib.auth.backends import ModelBackend


class ModelBackendHelper(ModelBackend):
    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_active:
            return False
        else:
            obj_perms = self.get_all_permissions(user_obj, obj)
            allowed = perm in obj_perms

            if not allowed:
                if settings.DEBUG:
                    similar = get_close_matches(perm, obj_perms)

                    if similar:
                        logging.warn("{0} not found, but is similar to: {1}".format(perm, ','.join(similar)))

            return allowed
  

Как это работает:

Та же логика has_perm , но если settings.DEBUG найдены is True и аналогичные версии perm , тогда выведите предупреждающее сообщение журнала уровня WARN :

 WARNING:root:myapp.view_tasks not found, but is similar to: myapp.view_task
  

Измените значение AUTHENTICATION_BACKENDS в settings.py :

 AUTHENTICATION_BACKENDS = ['myapp.backends.ModelBackendHelper']
  

Это можно использовать как в производственных средах, так и в средах разработки, но лично я бы не стал включать это в производственный сайт, я надеюсь, что когда все пойдет на производство, разрешения будут консолидированы.

Цель 2

Разрешения принадлежат моделям, и чтобы сохранить это СУХИМ, я повторно использую разрешения, определенные в Meta :

 from django.db import models


class Task(models.Model):
    name = models.CharField(max_length=30)
    description = models.TextField()

    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
        )


def _get_perm(model, perm):
    for p in model._meta.permissions:
        if p[0] == perm:
            return p[0]
    err = "Permission '{0}' not found in model {1}".format(perm, model)
    raise Exception(err)


def format_perm(perm):
    return '{0}.{1}'.format(__package__, perm)


def get_perm(model, type):
    return format_perm(_get_perm(model, type))


PERM_APP_VIEW_TASK = get_perm(Task, "view_task")
  

Доступ к разрешениям можно получить с get_perm помощью или с помощью ярлыка PERM_APP_VIEW_TASK :

 models.PERM_APP_VIEW_TASK
# or
get_perm(Task, "view_task")
# or
format_perm(_get_perm(Task, "view_task"))
  

В случае поиска отсутствующего разрешения get_perm вызовет Exception :

 PERM_APP_VIEW_TASK = get_perm(Task, "add_task")
  

Сообщение:

 Exception: Permission 'add_task' not found in model <class 'myapp.models.Task'>
  

Комментарии:

1. Разве это не показывает предупреждение во всех случаях, когда у пользователя нет разрешения, даже если разрешение написано правильно?

2. Как это работает, если вы используете несколько AUTHENTICATION_BACKENDS , например, с django-allauth? Разве вам не пришлось бы переопределять реализацию allauths’а?

Ответ №2:

Цель 1

Создайте пользовательский декоратор:

Внимание! непроверенный код!!!!!

 @permission_required
def assert_permission(permission):
    def real_decorator(original_function):
        def wrapper(request, *args, **kwargs):
            permission_object = Permission.objects.filter(codename=permission)
            if not permission_object.exists():
                raise ValueError("This permission does not exist!")
            original_function(request, *args, **kwargs)
        return wrapper
    return real_decorator
  

Цель 2

Добавляйте константы в свои классы моделей и обращайтесь к ним всякий раз, когда запрашиваете конкретные разрешения. пример:

 class Task(models.Model):
MY_PERMISSION_CONSTANT = 'app.view_task'
...
class Meta:
    permissions = (
        (MY_PERMISSION_CONSTANT, "Can see available tasks"),
    )

@permission_required(Task.MY_PERMISSION_CONSTANT)
def some_view(request):
  pass
  

Комментарии:

1. Вы уверены, что Цель1 не может быть достигнута? Для меня все разрешения в БД действительны. Каждая строка, которая не соответствует строке базы данных, является недопустимой.

2. @guettli не смог это проверить, но я предполагаю, что это должно сработать или, по крайней мере, направить вас в правильном направлении, если что-то не работает, и вы это исправите, пожалуйста, отредактируйте этот ответ 😉