我有一个 Django 系统,它使用 Guardian 的用户对象权限进行访问控制。我想编写一个函数,查找与“旧”用户关联的所有内容类型的所有权限,并将它们分配给"new"用户。我无法直接查询 UserObjectPermission
因为我们的系统使用直接 foreign key models出于性能原因。
这是一个我想要的示例
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
参数。并且,根据卫报文档
If more than one permission is present within sequence, their content type must be the same or MixedContentTypeError exception would be raised.
因此这不适用于具有不同内容类型的对象。有什么建议么?我还考虑过使用 get_user_perms但这也需要一个对象参数。
这是我们创建直接外键模型的方法
class MyModelUserObjectPermission(UserObjectPermissionBase):
content_object = models.ForeignKey(MyModel, on_delete=models.CASCADE)
后脚本
@willeM_ Van Onsem 解决方案在 SQLite 中运行良好,但在 MySQL 中引发以下错误。
此外,我们必须显式检查 UserObjectPermission 类(因为对于某些模型,我们仍在使用该类),并由于该模型具有不同的架构而以不同方式对待该模型
You can't specify target table 'tablename' for update in FROM clause"
我们必须将 ~Exists
更改为 .exclude()
。在 MySQL 中,您无法在同一语句中连接要更新的表
最佳答案
在这种情况下,我们可能可以“批量”执行此操作,例如每个 UserObjectPermissionBase 的子类一个查询。然而,有一个可能的警告:如果我们想将权限分配给新用户,并且该用户已经获得了该权限。
单个模型的查询如下所示:
<em>MyModelUserObjectPermission</em>.objects.filter(user=old_user).update(<b>user=new_user</b>)
但如果新用户的权限已存在,则此操作将会失败,因为这样存在唯一性约束,该约束将会失败。
但是我们可以look before we leap [wiki] ,其中:
from django.db.models import Exists, OuterRef
<em>MyModelUserObjectPermission</em>.objects.filter(
~Exists(
<em>MyModelUserObjectPermission</em>.objects.filter(
user=new_user,
content_object=OuterRef('content_object'),
permission=OuterRef('permission'),
)
),
user=old_user,
).update(user=new_user)
<em>MyModelUserObjectPermission</em>.objects.filter(user=old_user).delete()
因此,这将首先检查我们是否可以更改它,以便同一对象、权限和新用户不存在 MyModelUserObjectPermission
。如果是这种情况,我们会更新它。在第二个查询中,我们删除了仍然存在的旧用户的权限。
现在我们唯一需要做的就是对所有模型执行此操作,我们可以通过查找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)
<b>new_gen = {sc for k in new_gen for sc in k.__subclasses__()}</b>
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
时,该逻辑才有效。
关于django - 将所有 Guardian 用户对象权限从一个 Django 用户重新分配给另一个 Django 用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77431461/