将所有Guardian用户对象从一个Django用户重新分配给另一个Django用户

mcvgt66p  于 2023-11-20  发布在  Go
关注(0)|答案(1)|浏览(141)

我有一个Django系统,它使用Guardian的User Object Manager进行访问控制。我想写一个函数,查找与“旧”用户关联的所有内容类型的所有权限,并将其分配给“新”用户。我不能直接查询UserObjectPermission,因为我们的系统出于性能原因使用直接外键模型。
这是一个 * 我想要的 * 的例子

from django.contrib.auth.models import User
from guardian.shortcuts import get_objects_for_user, assign_perm

def update_permissions_for_user(old_user, new_user):
    # Retrieve all USER permission objects for the old user
    # this throws exception because it requires a perms param that I do not want to provide.  I want all objects across all models
    old_user_permissions = get_objects_for_user(old_user, use_groups=False, any_perm=False, with_superuser=False, accept_global_perms=False)

    # Iterate through each permission object and update it to point to the new user
    for permission in old_user_permissions:
        assign_perm(permission.codename, new_user, permission)
        remove_perm(permission.codename, old_user, permission)

    
# Example usage:
old_user = User.objects.get(username='old_username')  # Replace 'old_username' with the actual username
new_user = User.objects.get(username='new_username')  # Replace 'new_username' with the actual username

update_permissions_for_user(old_user, new_user)

字符串
如果我们不使用直接外键模型,代码将简单地是:

user_objects_permission_qs = UserObjectPermission.objects.filter(user=current_user)
affected_rows = user_objects_permission_qs.update(user=new_user)


然而,* 我想要的 * 示例的问题是,get_objects_for_user需要perms参数。
如果序列中存在多个权限,则它们的内容类型必须相同,否则将引发MixedContentTypeError异常。
所以这不能用于不同内容类型的对象。有什么建议吗?我也考虑过使用get_user_perms,但这也需要一个对象参数。
下面是我们如何创建直接外键模型

class MyModelUserObjectPermission(UserObjectPermissionBase):
    content_object = models.ForeignKey(MyModel, on_delete=models.CASCADE)

发布脚本

@willeM_货车Onsem解决方案在SQLite中运行良好,但在MySQL中引发以下错误。
此外,我们必须显式地检查UserObjectPermission类(因为对于某些模型,我们仍然使用它),并由于它具有不同的模式而以不同的方式对待这个模型

You can't specify target table 'tablename' for update in FROM clause"


我们必须将~Exists更改为.exclude()。在MySQL中,您不能在同一语句中连接要更新的表

p3rjfoxz

p3rjfoxz1#

在这种情况下,我们可能会在“bulk”中这样做,例如,UserObjectPermissionBase的每个子类都有一个查询。然而,有一个可能的警告:如果我们想将权限分配给新用户,并且用户已经获得了该权限。
单个模型的查询如下所示:

MyModelUserObjectPermission.objects.filter(user=old_user).update(user=new_user)

字符串
但如果新用户的许可已经存在,则这将失败,因为存在将失败的唯一性约束。
但是我们可以 look before we leap [wiki],使用:

from django.db.models import Exists, OuterRef

MyModelUserObjectPermission.objects.filter(
    ~Exists(
        MyModelUserObjectPermission.objects.filter(
            user=new_user,
            content_object=OuterRef('content_object'),
            permission=OuterRef('permission'),
        )
    ),
    user=old_user,
).update(user=new_user)
MyModelUserObjectPermission.objects.filter(user=old_user).delete()


因此,这将首先检查我们是否可以更改它,以便对于相同的对象,权限和新用户没有 MyModelUserObjectPermission。如果是这样,我们将更新它。在第二个查询中,我们将删除旧用户仍然存在的权限。
现在我们唯一需要做的就是为 all 模型做这件事,我们可以通过查找UserObjectPermissionBase的子类来做到这一点:

from django.db.models import Exists, OuterRef
from guardian.models.models import UserObjectPermissionBase

def change_perm_for_klass(klass, old_user, new_user):
    klass.objects.filter(
        ~Exists(
            klass.objects.filter(
                user=new_user,
                content_object=OuterRef('content_object'),
                permission=OuterRef('permission'),
            )
        ),
        user=old_user,
    ).update(user=new_user)
    klass.objects.filter(user=old_user).delete()

def update_permissions_for_user(old_user, new_user):
    subclasses = set()
    new_gen = {UserObjectPermissionBase}
    while new_gen:
        subclasses.update(new_gen)
        new_gen = {sc for k in new_gen for sc in k.__subclasses__()}
    for klass in subclasses:
        if not klass._meta.abstract:
            change_perm_for_klass(klass, old_user, new_user)


在这里,我们首先将类的层次结构隔离开来,寻找UserObjectPermissionBase的所有子类,接下来我们将为所有这些非抽象的模型调用change_perm_for_klass(…)
当然,只有当您通过定义UserObjectPermissionBase的子类来实现django-guardian时,逻辑才有效。

相关问题