我应该使用什么类型的模型设计来向用户添加角色,以便它在 Django 和 Django 管理中工作

发布于 2024-10-04 21:23:52 字数 2838 浏览 8 评论 0原文

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

示例:

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

在模型中可以通过各种(传统)方法来完成上述任务,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 的框架),但我找不到在管理中显示上述内容的正确方法。

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

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

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

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

为了解决最后一个问题,我做了一些修改,向空的用户角色添加了一个“虚拟”表单字段,然后使用 JS 隐藏该字段。这确实触发了 has_changed。

但这仍然行不通,因为在以后的一些测试中我的内联模型都没有被保存。

所以我认为我只是以错误的方式做事。我做了很多谷歌搜索,发现很多人都在解决同样的问题。最适合我的情况的是 http://www. mail-archive.com/[电子邮件受保护]/msg52867.html。但这个解决方案仍然没有给我一个简单的方法来实现管理。

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

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

class StudentUser(auth.models.User):
  pass

但是这里有两个问题。起初,当职员和教师是不可能的。其次,它在 Django 的其余部分中并没有真正起作用,因为请求对象返回一个 User 对象,而无法请求 Student、Parent、Staffmember 对象。获得其中之一的唯一方法是基于 User 对象实例化一个新的 Student 对象。

所以问题是:我应该使用什么类型的模型设计来向用户添加角色,以便它在 Django 和 Django 管理中工作?

友好的问候, 沃特

I am using Djangos default authentication system (django.contrib.auth) and I would like to add 'roles' to my users in such a way that Django Admin can work with it.

An example:

  • A user can be a staffmember, teacher, student and/or parent
  • If the user has a role assigned, he will gain permissions (eg. staffmembers may sign in to the Django admin)
  • Some roles might have some extra fields (eg. parent has a relation with at least one student and each student has a field with it's classgroup
  • Not every role has extra fields
  • A parent can be a teacher or staffmember and vise versa
  • A student can not have another role

There are all sorts of (conventional) ways to accomplish the above within a model. Django supports a lot of them, but the Django admin does not. The Django admin has a lot of good features so I would like to use it (but I am getting more and more afraid that it will not be possible).

The following model is what I thought of at first:

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")

This works in Django (and in a lot of other MVC-based frameworks). But I can't find a proper way to display the above in the admin.

Ideally I would like to add a User and then within the User-changeview add different roles. At first I thought I could use Inlines:

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

I then make some slight changes to the inline template to present the editing user button with a caption 'Add Student role'. Once we hit the button, we display the form and a User role is added. Not ideal, but it works.

Too bad, for Staffmembers there are no fields to add to the inline form. This way it is not possible to trigger the 'has_changed' property for inlines forms. This results in the new role not being saved to the database.

To solve this last problem, I hacked around a bit and added a 'dummy' formfield to the empty user-roles and then hide this field using JS. This did trigger the has_changed.

Still this would not work for somehow none of my inline-models are saved during some tests later on.

So I think I am just doing it the wrong way. I did a lot of Googling and found a lot of people hassling with the same sorts of problems. The one that suited my situation the most was http://www.mail-archive.com/[email protected]/msg52867.html. But still this solution does not give me an easy way to implement the admin.

I also thought about using the built-in groups but in that case I have no idea how I should add the different fields.

Another thing I thought of was trying to 'Add a student' instead of adding a User and assigning a role to him. This works pretty well in the admin if you just inherit the user:

class StudentUser(auth.models.User):
  pass

But two problems here. At first it is not possible to be a staffmember and a teacher. Second it is not really working in the rest of Django for the request object return a User object for which it is impossible to request the Student, Parent, Staffmember object. The only way to get one of these is to instantiate a new Student object bases on the User object.

So here is the question: what type of model design should I use to add roles to Users in such a way that it works in Django and in the Django admin?

Friendly Regards,
Wout

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

反目相谮 2024-10-11 21:23:52

我假设您不想更改管理员,或复制 django.contrib.admin 并根据需要对其进行破解。

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

您可以将其存储在 用户个人资料。从 User 继承也可以,但是您需要手动从 User 映射到 User.get_profile(),而不是使用 User.get_profile()代码>扩展用户。

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

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

我能想到的最自动的方法是创建一个“更新权限” admin 命令,它将更新管理字段,例如 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 清晰得多。 code> (我将其读作“用户的老师”)。

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

您将在管理员中为每个角色拥有一张表。没有什么奇特的“当你改变角色时编辑页面的下半部分会重新绘制”功能。如果您想要这样,您将需要编写自己的管理员。

Django 的管理很棒,但它并没有试图满足所有人的一切。我有一个像你一样的基于角色的设置(除了角色本身存储在表中),并且管理员工作正常,但有点笨重。总的想法是,如果管理员不够好,那么你应该写下自己的观点。

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

内置组不是您要寻找的。

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

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

其次,它在 Django 的其余部分中并没有真正起作用,因为请求对象返回一个 User 对象,而无法请求 Student、Parent、Staffmember 对象。获得其中之一的唯一方法是基于 User 对象实例化一个新的 Student 对象。

不,您可以使用从 User 对象自动生成的 id 来查找 Student 对象:

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 或它们的任何条目。

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

I'm assuming in the following that you do not want to alter the admin, or make a copy of django.contrib.admin and hack it as desired.

A user can be a staffmember, teacher, student and/or parent

You could store this in a user profile. Inheriting from User will work, too, but instead of using User.get_profile(), you'll need to manually map from User to ExtendedUser.

If the user has a role assigned, he will gain permissions (eg. staffmembers may sign in to the Django admin)

In that specific case, you can't use automatic role-based assignment. Whether or not a person can access the admin is determined by the is_staff field in their User record.

The most automatic way I can think of is to create an "Update Permissions" admin command, which will update admin fields like is_staff and the permissions based on the role set in the user's profile. BTW, even though this is not fully "automatic", it is a denormalization that can improve performance.

Some roles might have some extra fields (eg. parent has a relation with at least one student and each student has a field with it's classgroup

Not every role has extra fields

A parent can be a teacher or staffmember and vise versa

A student can not have another role

Read up on form validation. That's where you can enforce these rules.

In your model, I'd recommend that you alter the related names of your one-to-one fields:

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")

Since theUser.as_teacher is a lot clearer than theUser.teacher (which I would read as "the user's teacher").

This works in Django (and in a lot of other MVC-based frameworks). But I can't find a proper way to display the above in the admin.

You're going to have one table in the admin per role. There's no fancy "bottom half of the edit page will redraw itself when you change roles" feature. If you want that, you will need to write your own admin.

Django's admin is great, but it's not trying to be everything to everyone. I have a role-based setup like yours (except the roles themselves are stored in a table), and the admin works fine if a little clunky. The general idea is that if the admin isn't good enough, then you should be writing your own views.

I also thought about using the built-in groups but in that case I have no idea how I should add the different fields.

The built-in groups are not what you're looking for.

Another thing I thought of was trying to 'Add a student' instead of adding a User and assigning a role to him. [...] At first it is not possible to be a staffmember and a teacher.

"Subclassing" is a more restrictive one-to-one. I think your initial model is better.

Second it is not really working in the rest of Django for the request object return a User object for which it is impossible to request the Student, Parent, Staffmember object. The only way to get one of these is to instantiate a new Student object bases on the User object.

No, you instead find the Student object using the auto-generated id from the User object:

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

If you're going to use the admin, I think you're going to have to live with a two-step process of adding an ExtendedUser, then adding Student or whatever entries for them.

So it comes down to a tradeoff: a little extra work using the built-in admin, or writing your own user management views. WHich route is best really depends on how much this interface will be used: If it's just you, then the admin should be fine, even with its warts. If a lot of people will be using it on a routine basis, then you should just write your own views to handle things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文