django guardian https://github.com/lukaszb/django-guardian是一个写得很好的对象级权限应用程序;我已经阅读并在各种django项目中使用了相当多的其他django对象级权限应用程序。
在我最近的一个项目中,我决定使用django监护人,但我有一个模型设计问题,涉及两种可能方法的优缺点及其对sql查询的各自影响性能: -
使用django.contrib.auth.models.Group
并将其扩展到我的自定义组织应用的模型;或
改为使用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_A
,sc_B
等可能包含很多组。
这意味着,为了确定特定用户(比如myuser
)是否属于某个特定组织,我必须通过{{1来查询该用户所属的所有组。然后通过groups_myuser_belongto = myuser.groups
查询与我感兴趣的组织相关联的所有组,因为我现在有2个需要比较的列表,我可以groups_studentclass = sc_A.groups.all()
返回一个新集合,其中可能包含一个或多个相交的组。如果有1个或多个组,则set(groups_myuser_belongto) && set(groups_studentclass)
确实是myuser
的成员。
因此,这个模型设计意味着我必须经历很多麻烦(和额外的查询)才能找出用户是否属于某个组织。
我将m2m用于组的原因是为了使用django guardian提供的组级权限功能。
这样的模型设计是否实用?
或者我最好选择不同的模型设计......
方法#2
sc_A
显然,这种模型设计使我很容易检查用户对象是否属于某个特定组织。我需要做的就是找出用户# 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)
是否属于TeachingTeam john
的一部分是检查:
maths_teachers
但是这个模型设计暗示的是当我将一个用户对象添加到一个组时(回想一下我想使用django guardian的组权限功能),我必须确保{{ 1}}调用将用户对象添加到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!"
AND 中的“数学教师”组中,并添加到我的自定义save()
班级的“数学教师”对象中。这并不觉得很干,更不用说我必须以某种方式确保两个模型中的保存调用都在一个django.contrib.auth.models.Group
中完成。
根据这个用例/要求,有没有更好的方法来设计我的模型 - 使用django组并提供一种方法来“扩展”django的本机组功能(几乎就像我们使用“用户配置文件”扩展django的用户模型一样应用程序“)?
答案 0 :(得分:7)
我对此的看法(长期开发django应用程序)是你应该坚持自然的方法(所以StudentClass有用户而不是群组)。这里“自然”意味着它对应于所涉及对象的实际语义。
如果属于特定的StudentClass必须暗示某个自动组(除了授予用户的那些),请向StudentClass模型添加groups
m2m,并创建新的身份验证后端(扩展默认后端) ,它提供自定义get_all_permissions(self, user_obj, obj=None)
方法。它将被https://github.com/django/django/blob/master/django/contrib/auth/models.py#L201
在此方法中查询与用户所属的任何组织关联的任何组。而且您不需要执行1 + N个查询,正确使用ORM将同时导航到两个*到多个。
https://github.com/django/django/blob/master/django/contrib/auth/backends.py#L37中的当前ModelBackend方法查询get_group_permissions(user_obj)
并将它们添加到用户分配的权限中。您可以通过添加(缓存)get_student_class_permission
和其他相应的方法来添加类似的行为。
(为更清晰的序幕编辑)
答案 1 :(得分:0)
Obs:还有另一种方法是使用generic relationships,在这种方法中,你可以让User模型实例通过contenttypes框架指向它的资源。有nice question here on SE解释这种方法。
关于绩效:有一些线索表明,使用JOIN的单个选择比许多简单选择(1,2,3)便宜。在这种情况下,选择2会更好。
关于可用性:第一种方法难以解释,难以理解。恕我直言不。 2.或尝试一般关系。