我有一个Django系统,它使用Guardian的用户对象权限.我想要编写一个函数,该函数可以查找与"旧"用户相关联的所有内容类型的所有权限,并将它们分配给"新"用户.我不能直接查询UserObjectPermission,因为出于性能原因,我们的系统使用直接foreign key models.

这是example of what I want美元

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)

然而,example of what I want的问题是get_objects_for_user需要perms参数.而且,根据《卫报》的文件

如果Sequence中存在多个权限,则它们的内容类型必须相同,否则将引发MixedContent TypeError异常.

因此,这不适用于具有不同内容类型的对象.有什么建议吗?我也考虑过使用get_user_perms,但这也需要一个对象参数.

下面是我们如何创建直接外键模型

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

推荐答案

在这种情况下,我们可能会在"批量"中这样做,就像在UserObjectPermissionBase的一个查询per子类中一样.然而,有一个可能的警告:如果我们想将权限分配给新用户,并且用户已经获得了该权限.

单个模型的查询如下所示:

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()

因此,这将首先判断我们是否可以更改它,以便相同的对象、权限和新用户没有100.如果是这样的话,我们会更新它.在第二个查询中,我们随后删除了旧用户中仍然存在的权限.

现在我们唯一需要做的就是对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)

因此,在这里,我们首先划分类层次 struct ,以查找UserObjectPermissionBase的所有子类,接下来,我们将为所有非抽象的模型调用change_perm_for_klass(…).

当然,只有在您通过定义UserObjectPermissionBase的子类来实现django-guardian的情况下,这种逻辑才有效.

Django相关问答推荐

Django中的Sync_to_Async修饰器、异步视图

__init__()获取了意外的关键字参数';id';

Django 仅预取相关模型的最新对象

Django ModelForm提交按钮不起作用

相同的字符串不同的翻译

模板过滤器修剪任何前导leading或尾随trailing空格

Django判断字段是否为空?

如何从 django 自定义中间件类返回 rest_framework.response 对象?

Django - 在模板中显示当前日期和时间

如何获取 Django 模型来自的应用程序?

django - pisa:将图像添加到 PDF 输出

django- nginx: [emerg] open() "/etc/nginx/proxy_params" 在 /etc/nginx/sites-enabled/myproject:11 中失败(2:没有这样的文件或目录)

如何按名称设置 django 模型字段?

可以在 Django 抽象模型中使用多重继承吗?

Django:在管理界面中显示图像

如何使用 django-nose 运行单个测试或单个 TestCase?

Django 和 VirtualEnv 开发/部署最佳实践

Django 什么是反向关系?

有没有一种简单的方法可以从 CharField 填充 SlugField?

如何在 django 模板中呈现有序字典?