8

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是否是教学团队的一员,我需要做的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 的组权限功能),我必须确保save()调用将用户对象添加到“数学教师”组中在django.contrib.auth.models.Group AND到我的自定义TeachingTeam课程的“数学教师”对象中。这感觉不是很干,更不用说我必须以某种方式确保对两个模型的保存调用都在一个transaction.

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

4

2 回答 2

8

我对此的看法(已经开发了很长时间的 django 应用程序)是你应该坚持自然的方法(所以 StudentClass 有用户而不是组)。这里的“自然”意味着它对应于所涉及对象的实际语义。

如果属于特定的 StudentClass 必须隐含一些自动组(除了授予用户的组),添加一个groupsm2m 到 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和其他相应的方法来添加类似的行为。

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

于 2012-06-22T14:10:25.683 回答
0

Obs:还有另一种方法是使用通用关系,在这种方法中,您可以让 User 模型实例通过 contenttypes 框架指向它的资源。SE 上有一个很好的问题来解释这种方法。

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

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

于 2012-06-26T02:01:21.803 回答