代码之家  ›  专栏  ›  技术社区  ›  Wouter Klein Heerenbrink

我应该使用哪种类型的模型设计来向用户添加角色,使其在Django和Django管理中工作

  •  0
  • Wouter Klein Heerenbrink  · 技术社区  · 14 年前

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

    一个例子:

    • 用户可以是 员工人数 , 老师 , 学生 和/或 起源
    • 如果用户分配了一个角色,他将获得权限(例如 工作人员 可以登录到django管理员)
    • 有些角色可能有一些额外的字段(例如 起源 与至少一个 学生 每个 学生 有一个字段和它的ClassGroup
    • 不是每个角色都有额外的字段
    • 起源 可以是一个 老师 员工人数 反之亦然
    • 学生 不能有其他角色

    在一个模型中,有各种(传统的)方法来实现上述目标。Django支持很多,但Django管理员不支持。Django管理员有很多好的特性,所以我想使用它(但我越来越担心它不可能实现)。

    以下模型是我最初想到的:

    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的框架)中工作。但是我找不到一个合适的方法来在管理员中显示上面的内容。

    理想情况下,我想添加一个用户,然后在用户变更视图中添加不同的角色。一开始我想我可以使用inlines:

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

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

    太糟糕了,对于staffmembers,没有要添加到内联表单中的字段。这样就不可能为内联表单触发“has-changed”属性。这将导致新角色未保存到数据库中。

    为了解决最后一个问题,我进行了一些修改,在空用户角色中添加了一个“dummy”formfield,然后使用JS隐藏这个字段。这确实触发了改变。

    不过,这仍然不起作用,因为稍后的一些测试中没有保存任何内联模型。

    所以我想我做得不对。我做了很多谷歌搜索,发现很多人都在为同样的问题争论。最适合我的是 http://www.mail-archive.com/django-users@googlegroups.com/msg52867.html . 但是这个解决方案仍然不能给我一个简单的方法来实现管理。

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

    我想的另一件事是尝试“添加一个学生”,而不是添加一个用户并为他分配一个角色。如果您只是继承了用户,那么这在管理中非常有效:

    class StudentUser(auth.models.User):
      pass
    

    但这里有两个问题。一开始是不可能成为一名职员和一名教师的。其次,它在Django的其余部分并不真正起作用,因为请求对象返回一个用户对象,对于该用户对象,不可能请求student、parent、staffmember对象。获得其中一个的唯一方法是基于用户对象实例化一个新的学生对象。

    所以这里有一个问题:我应该使用什么类型的模型设计来向用户添加角色,从而使它在django和django管理中工作?

    致以友好的问候, 沃特

    1 回复  |  直到 14 年前
        1
  •  2
  •   Mike DeSimone    14 年前

    我在下面假设您不想更改管理员或复制 django.contrib.admin 并根据需要进行黑客攻击。

    用户可以是职员、教师、学生和/或家长。

    你可以把这个放在 user profile . 继承自 User 也可以,但不是使用 User.get_profile() ,您需要从手动映射 用户 ExtendedUser .

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

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

    我能想到的最自动的方法是创建一个“更新权限” admin command ,它将更新管理字段,如 是员工吗? 以及基于用户配置文件中的角色集的权限。顺便说一句,尽管这不是完全“自动”的,但它是一种可以提高性能的非规范化。

    有些角色可能有一些额外的字段(例如,家长与至少一个学生有关系,每个学生都有一个与其所在班级的字段)

    不是每个角色都有额外的字段

    父母可以是教师或职员,反之亦然。

    学生不能有其他角色

    阅读 form validation . 在那里你可以执行这些规则。

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

    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_teacher theUser.teacher (我读作“用户的老师”)。

    这在Django(以及许多其他基于MVC的框架)中工作。但是我找不到一个合适的方法来在管理员中显示上面的内容。

    您将在每个角色的管理员中拥有一个表。“当您更改角色时,编辑页面的下半部分将自动重新绘制”这一特性是不可取的。如果你想要的话,你需要写你自己的管理员。

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

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

    内置组不是你想要的。

    我想的另一件事是尝试“添加一个学生”,而不是添加一个用户并为他分配一个角色。[…]一开始是不可能成为一名职员和一名教师的。

    “子类化”是一个更严格的一对一。我觉得你的初始模型更好。

    其次,它在Django的其余部分并不真正起作用,因为请求对象返回一个用户对象,对于该用户对象,不可能请求student、parent、staffmember对象。获得其中一个的唯一方法是基于用户对象实例化一个新的学生对象。

    不,你可以找到 Student 使用自动生成的 id 来自 用户 对象:

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

    如果你要使用管理员, 我认为你将不得不经历一个两步的过程来增加 延长除尘器 ,然后添加 学生 或者任何条目。

    因此,归根结底是一种权衡:使用内置的管理进行一些额外的工作,或者编写自己的用户管理视图。哪条路线最好,真正取决于这个界面将被使用多少:如果只是你一个人,那么管理员应该很好,即使有缺点。如果很多人将在常规基础上使用它,那么您应该只写自己的观点来处理事情。