【问题标题】:django guardian, permissions and extending django auth groups to 'organization' modelsdjango 守护者、权限和将 django auth 组扩展到“组织”模型
【发布时间】:2012-05-03 17:01:51
【问题描述】:

django Guardian https://github.com/lukaszb/django-guardian 是一个写得很好的对象级权限应用;我实际上已经在各种 django 项目中阅读并使用了许多其他 django 对象级权限应用程序。

在我最近从事的一个项目中,我决定使用 django Guardian,但我有一个模型设计问题,涉及两种可能方法的优缺点以及它们各自对 sql 查询的影响性能:-

  1. 使用 django.contrib.auth.models.Group 并将其扩展到我的自定义组织应用程序的模型;或

  2. 改用 django.contrib.auth.models.User 并为我的组织应用程序中的每个组织类型创建一个 m2m 字段。

方法#1

# Organisation app's models.py

from django.contrib.auth.models import Group

class StudentClass(models.Model):
    name = models.CharField('Class Name', max_length=255)
    groups = models.ManyToManyField(Group, blank=True)
    size = models.IntegerField('Class Size', blank=True)

class SpecialInterestGroup(models.Model):
    name = models.CharField('Interest Group Name', max_length=255)
    groups = models.ManyToManyField(Group, blank=True)
    description = models.TextField('What our group does!', blank=True)

class TeachingTeam(models.Model):
    name = models.CharField('Teacher Team Name', max_length=255)
    groups = models.ManyToManyField(Group, blank=True)
    specialization = models.TextField('Specialty subject matter', blank=True)

在这种方法中,当用户第一次添加到组(django 组)时,如果该组对象不属于该类,则会创建组对象并将其分配给这 3 个类之一它被添加到。

这意味着每个 StudentClass 对象,sc_Asc_B 等都可能包含很多组。

这意味着我要确定特定用户(例如 myuser)是否属于特定组织,我必须通过 groups_myuser_belongto = myuser.groups 查询该用户所属的所有组,然后通过groups_studentclass = sc_A.groups.all() 查询与我感兴趣的组织相关联的所有组,因为我现在有 2 个需要比较的列表,我可以执行set(groups_myuser_belongto) && set(groups_studentclass),这将返回一个可能包含 1 的新集合或更多相交的组。如果有 1 个或多个组,则 myuser 确实是 sc_A 的成员。

因此,这种模型设计意味着我必须经历很多麻烦(和额外的查询)才能确定用户是否属于某个组织。

我之所以使用 m2m 到群组,是为了利用 django Guardian 提供的群组级别权限功能。

这样的模型设计实用吗?

或者我最好还是采用不同的模型设计...

方法 #2

# Organisation app's models.py

from django.contrib.auth.models import User

class StudentClass(models.Model):
    name = models.CharField('Class Name', max_length=255)
    users = models.ManyToManyField(User, blank=True)
    size = models.IntegerField('Class Size', blank=True)

class SpecialInterestGroup(models.Model):
    name = models.CharField('Interest Group Name', max_length=255)
    users = models.ManyToManyField(User, blank=True)
    description = models.TextField('What our group does!', blank=True)

class TeachingTeam(models.Model):
    name = models.CharField('Teacher Team Name', max_length=255)
    users = models.ManyToManyField(User, blank=True)
    specialization = models.TextField('Specialty subject matter', blank=True)

显然,这种模型设计让我很容易检查用户对象是否属于特定组织。要确定用户 john 是否是 TeachingTeam maths_teachers 的一部分,我需要做的就是检查:

user = User.objects.get(username='john')
maths_teachers = TeachingTeam.objects.get(name='Maths teachers')
if user in maths_teachers.users.all():
    print "Yes, this user is in the Maths teachers organization!"

但是这个模型设计意味着 当我将一个用户对象添加到一个组时(回想一下我想使用 django Guardian 的组权限功能),我必须确保 @987654337 @call 将用户对象添加到django.contrib.auth.models.Group 中的“数学教师”组中AND 到我的自定义TeachingTeam 类的“数学教师”对象中。这感觉不是很干,更不用说我必须以某种方式确保对两个模型的保存调用都在单个 transaction 中完成。

鉴于此用例/要求,是否有更好的方法来设计我的模型 - 使用 django 组,但提供一种“扩展”django 的本机组功能的方法(几乎就像我们如何使用“用户配置文件”扩展 django 的用户模型一样)应用”)?

【问题讨论】:

  • 在将对象与组链接时为什么不使用OneToOne
  • 因为与 User-to-Userprofile 关系(使用 OneToOneForeignKey,因为 1 个用户配置文件可能只与 1 个用户相关)不同,“组织”类可以与许多团体相关联。
  • 那么 Django 组是什么意思呢?我认为您的组应该像用户配置文件一样继承 Django 的组(当然,我的意思是第一种方法)
  • django 组可能意味着特定类型组织中的不同组。例如,StudentClass 可以包含多个项目组。

标签: django django-models rbac


【解决方案1】:

我对此的看法(长期开发 django 应用程序)是您应该坚持使用自然方法(因此 StudentClass 有用户而不是组)。这里的“自然”意味着它对应于所涉及对象的实际语义。

如果属于特定的 StudentClass 必须隐含一些自动组(除了授予用户的组),添加 groups m2m 到 StudentClass 模型,并创建一个新的身份验证后端(扩展默认的),这提供了一个自定义的get_all_permissions(self, user_obj, obj=None) 方法。会被https://github.com/django/django/blob/master/django/contrib/auth/models.py#L201上钩

在此方法中查询与用户所属的任何组织关联的任何组。而且您不需要进行 1+N 查询,正确使用 ORM 将同时浏览两个 *-to-many。

https://github.com/django/django/blob/master/django/contrib/auth/backends.py#L37 中的当前 ModelBackend 方法查询 get_group_permissions(user_obj) 并将它们添加到用户分配的权限中。您可以通过添加(缓存)get_student_class_permission 和其他相应方法来添加类似的行为。

(为更清晰的序言而编辑)

【讨论】:

  • 是的,如果您不需要那种粒度控制,组概念会让事情变得更加复杂
  • 这非常有用。确保您要么从 ModelBackend 继承,要么为 get_permget_all_permissions 添加方法,即使它们只是调用 get_group_permissions
【解决方案2】:

Obs:还有另一种方法是使用generic relationships,在这种方法中,您可以让用户模型实例通过内容类型框架指向它的资源。有一个nice question here on SE 解释了这种方法。

关于性能:有一些线索表明,带有 JOIN 的单个 select 比许多简单 select 便宜 (1,2,3)。在这种情况下,选项 2 会更好。

关于可用性:第一种方法很难解释,很难理解。恕我直言,不要。 2. 或者试试泛型关系。

【讨论】:

    猜你喜欢
    • 2012-10-05
    • 2012-06-21
    • 2019-09-15
    • 1970-01-01
    • 2019-03-05
    • 2016-07-10
    • 1970-01-01
    • 2016-07-31
    • 2020-12-19
    相关资源
    最近更新 更多