0

我正在使用 Django 的默认身份验证系统 (django.contrib.auth),我想以 Django Admin 可以使用的方式向我的用户添加“角色”。

一个例子:

  • 用户可以是工作人员教师学生和/或家长
  • 如果用户分配了角色,他将获得权限(例如,员工可以登录 Django 管理员)
  • 某些角色可能有一些额外的字段(例如,父母与至少一个学生有关系,并且每个学生都有一个与其班级组相关的字段
  • 并非每个角色都有额外的字段
  • 家长可以是老师工作人员,反之亦然
  • 学生不能担任其他角色

有各种(常规)方法可以在模型中完成上述任务。Django 支持很多,但 Django 管理员不支持。Django admin 有很多很好的功能,所以我想使用它(但我越来越害怕它不可能)。

下面的模型是我一开始想到的:

class ExtendedUser(contrib.auth.models.User):
    """
       For the ease of use I inherit from User. I might
       want to add methods later
    """
    pass

class StaffMember(models.Model):
    """
       A staffmember is a co-worker of the organisation
       and has permissions to make changes to (parts of)
       the system.

       For now the staffmember only has methods
    """
    user = models.OneToOneField(ExtendedUser, related_name="staff_member")

class Student(models.Model):
    """
      A student can participate in some courses
    """
    user = models.OneToOneField(ExtendedUser, related_name="student")

class Teacher(models.Model):
   user = models.OneToOneField(ExtendedUser, related_name="teacher")
   subjects = models.ManyToManyField(..)

class Parent(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="parent")
    children = models.ManyToManyField(Student, related_name=parents")

这适用于 Django(以及许多其他基于 MVC 的框架)。但我找不到在管理员中显示上述内容的正确方法。

理想情况下,我想添加一个用户,然后在用户更改视图中添加不同的角色。起初我以为我可以使用内联:

class StudentInlineAdmin(admin.StackedInline):
    model = Student
    extra = 0
    template = 'accounts/admin/role.html'

然后,我对内联模板进行了一些细微的更改,以显示带有标题“添加学生角色”的编辑用户按钮。一旦我们点击按钮,我们就会显示表单并添加一个用户角色。不理想,但它有效。

太糟糕了,对于员工来说,没有字段可以添加到内联表单中。这样就不可能触发内联表单的“has_changed”属性。这会导致新角色未保存到数据库中。

为了解决最后一个问题,我稍微修改了一下,在空的用户角色中添加了一个“虚拟”表单域,然后使用 JS 隐藏这个域。这确实触发了 has_changed。

但这仍然无法在以后的一些测试中以某种方式保存我的内联模型。

所以我认为我只是做错了。我做了很多谷歌搜索,发现很多人都在为同样的问题而烦恼。最适合我的情况是http://www.mail-archive.com/django-users@googlegroups.com/msg52867.html。但是这个解决方案仍然没有给我一个简单的方法来实现管理员。

我也考虑过使用内置组,但在这种情况下,我不知道应该如何添加不同的字段。

我想到的另一件事是尝试“添加学生”而不是添加用户并为他分配角色。如果您只是继承用户,这在管理员中效果很好:

class StudentUser(auth.models.User):
  pass

但是这里有两个问题。起初,不可能既是工作人员又是老师。其次,在 Django 的其余部分中,请求对象返回的用户对象实际上并不能正常工作,无法为其请求学生、父母、员工对象。获得其中之一的唯一方法是基于 User 对象实例化一个新的 Student 对象。

所以这里有一个问题:我应该使用哪种类型的模型设计来向用户添加角色,以便它在 Django 和 Django 管理员中工作?

友好的问候, Wout

4

1 回答 1

2

我在下面假设您不想更改管理员,或者根据需要复制django.contrib.admin和破解它。

用户可以是工作人员、教师、学生和/或家长

您可以将其存储在用户配置文件中。继承 fromUser也可以,但User.get_profile()您需要手动映射 fromUser而不是使用ExtendedUser

如果用户分配了角色,他将获得权限(例如,员工可以登录 Django 管理员)

在这种特定情况下,您不能使用基于角色的自动分配。一个人是否可以访问管理员取决于他们记录is_staff中的字段。User

我能想到的最自动的方法是创建一个“更新权限”管理命令,它将is_staff根据用户配置文件中设置的角色更新管理字段和权限。顺便说一句,尽管这不是完全“自动”的,但它是一种可以提高性能的非规范化。

某些角色可能有一些额外的字段(例如,父母与至少一个学生有关系,并且每个学生都有一个与其班级组相关的字段

并非每个角色都有额外的字段

家长可以是老师或工作人员,反之亦然

学生不能担任其他角色

阅读表单验证。这就是您可以执行这些规则的地方。

在您的模型中,我建议您更改一对一字段的相关名称:

class StaffMember(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_staff_member")

class Student(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_student")

class Teacher(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_teacher")

class Parent(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_parent")

因为theUser.as_teachertheUser.teacher(我会读为“用户的老师”)清楚得多。

这适用于 Django(以及许多其他基于 MVC 的框架)。但我找不到在管理员中显示上述内容的正确方法。

您将在每个角色的管理员中拥有一张表。没有花哨的“当您更改角色时,编辑页面的下半部分会自行重绘”功能。如果需要,您将需要编写自己的管理员。

Django 的管理员很棒,但它并没有试图成为所有人的一切。我有一个像你一样的基于角色的设置(除了角色本身存储在一个表中),如果有点笨拙,管理员可以正常工作。一般的想法是,如果管理员不够好,那么您应该编写自己的视图。

我也考虑过使用内置组,但在这种情况下,我不知道应该如何添加不同的字段。

内置组不是您想要的。

我想到的另一件事是尝试“添加学生”而不是添加用户并为他分配角色。[...] 起初,不可能既是工作人员又是教师。

“子类化”是一种更具限制性的一对一。我认为您的初始模型更好。

其次,在 Django 的其余部分中,请求对象返回的用户对象实际上并不能正常工作,无法为其请求学生、父母、员工对象。获得其中之一的唯一方法是基于 User 对象实例化一个新的 Student 对象。

不,您改为使用从对象Student自动生成的对象来查找对象:idUser

try:
   theStudent = Student.objects.get(user_ptr_id=theUser.id)
except Student.DoesNotExist:
   # That user isn't a student; deal with it here.

如果您要使用管理员,我认为您将不得不忍受一个两步的过程,即添加一个ExtendedUser,然后Student为它们添加或任何条目。

所以它归结为一个权衡:使用内置管理员的一些额外工作,或者编写自己的用户管理视图。哪条路线最好真的取决于这个界面的使用量:如果只有你,那么管理员应该没问题,即使有它的缺点。如果很多人会日常使用它,那么您应该编写自己的视图来处理事情。

于 2010-11-27T13:26:37.327 回答