将 prefetch_related() 添加到 Django 身份验证后端

Add prefetch_related() to Django Authentication backend

在我的项目中,我同时拥有基于组和自定义用户的权限。

我有一个自定义身份验证后端,它主要检查用户是否具有组权限,然后查看他们是否有任何需要从已检查权限中删除的已撤销权限。

现在我正在 运行 遇到一个优化问题,因为我正在测试上述撤销权限的实现,因为我的 CustomUser 模型有一个 M2M 字段,其中包含与 [=13= 相关的这些撤销权限],我的 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)

我想出了一个解决方案来完成我需要的,尽管它是一个有点不错的解决方法...

我将 "Revoked Permissions" 更改为 "Extra Applied Permissions"(出于要求原因)并将模型字段从 M2M 关系更改为存储权限 content_type.app_labelcodename 作为连接字符串,然后我将其用于比较。

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)