Добавить prefetch_related () в Django бэкэнд аутентификации - PullRequest
0 голосов
/ 05 мая 2020

В моем проекте у меня есть как групповые, так и настраиваемые разрешения на основе пользователей.

У меня есть настраиваемый бэкэнд аутентификации, который по сути проверяет, есть ли у пользователя разрешения группы, а затем проверяет, есть ли у них отозванные разрешения, которые необходимы быть удаленным из проверенных разрешений.

Я столкнулся с проблемой оптимизации сейчас, когда я тестирую реализацию указанного отозванного разрешения, потому что моя модель CustomUser имеет поле M2M, которое содержит эти отозванные разрешения, что является отношением на auth_permissions, и моя BackendAuthentication проверяет это, я получаю сумасшедшее количество обращений к БД при загрузке страницы.

Как я могу передать предварительно выбранный объект в свой AuthBackend?

Вот мой AuthBackend:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import Permission

class UsersAuthenticationBackend(ModelBackend):
    def _get_revoked_perms(self, user_obj):
        if user_obj.is_superuser or user_obj.is_admin:
            revoked_perms = Permission.objects.none()
        elif hasattr(user_obj, 'revoked_permissions'):
            # this causes the issue, I need to pass in the prefetch related to my model backend...HOW?
            # this should be something like CustomUser.objects.prefetch_related('revoked_permissions')
            revoked_perms = getattr(user_obj, 'revoked_permissions').values_list('content_type__app_label', 'codename')
        else:
            revoked_perms = Permission.objects.none()

        revoked_perms = ["{}.{}".format(perm[0], perm[1]) for perm in revoked_perms]
        print(revoked_perms)
        return revoked_perms

    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_active:
            return False

        revoked_perms = self._get_revoked_perms(user_obj)
        all_perms = self.get_all_permissions(user_obj)
        allowed_perms = [p for p in all_perms if not p in revoked_perms]

        if isinstance(perm, str):
            return perm in allowed_perms
        elif isinstance(perm, Permission):
            return '{}.{}'.format(perm.content_type.app_label, perm.codename) in allowed_perms
        else:
            return False

Вот соответствующая часть CustomUser, если вам нужно ее увидеть

class CustomUser(AbstractUser, SafeDeleteModel):
    ...
    revoked_permissions = models.ManyToManyField(Permission, blank=True)

1 Ответ

0 голосов
/ 06 мая 2020

Я нашел решение для выполнения sh того, что мне было нужно, хотя это был довольно приличный обходной путь ...

Я изменил «Отмененные разрешения» на «Дополнительные применяемые разрешения» (по причинам требований ) и изменил поле модели с отношения M2M на JSONField (список), который хранит разрешения content_type.app_label и codename в виде объединенной строки, которую я затем использую для сравнения.

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import Permission

class UsersAuthenticationBackend(ModelBackend):
    def _get_allowed_perms(self, user_obj):
        if user_obj.is_superuser or user_obj.is_admin:
            allowed_perms = []
        elif hasattr(user_obj, 'extra_allowed_permissions'):
            allowed_perms = user_obj.extra_allowed_permissions
        else:
            allowed_perms = []
        # 'content_type__app_label'.'codename'
        allowed_perms = [perm for perm in allowed_perms]
        # print(allowed_perms)
        return allowed_perms

    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_active:
            return False

        allowed_perms = self._get_allowed_perms(user_obj)
        group_perms = self.get_group_permissions(user_obj)
        # concat the perms for comparison
        combined_perms = list(group_perms) + allowed_perms

        if isinstance(perm, str):
            return perm in combined_perms
        elif isinstance(perm, Permission):
            perm_string = '{}.{}'.format(perm.content_type.app_label, perm.codename)
            return perm_string in combined_perms
        else:
            return False

Вот Изменение модели:

class CustomUser(AbstractUser, SafeDeleteModel):
    ...
    extra_allowed_permissions = JSONField(default=list, null=True, blank=True)

...