Django:实现多用户级别/角色/类型

Kyl*_*397 6 python mysql database django web

我使用 Django 已经有一段时间了,但直到现在我才想到这一点。

目前,我有一个包含不同用户级别的项目。通常,在我过去的经验中,我只使用 Django 开发系统,只有两个用户级别,即超级用户和普通/普通用户。所以我的问题是在模型/数据库中呈现这些不同用户级别的有效方法什么?在这里,我将使用学校系统作为示例,并提供我对实施它的一些初步想法。

用户等级:

  1. 管理员(超级用户和员工)
  2. 主要的
  3. 老师
  4. 学生们

方法#1:根据每个用户级别添加新表

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    user = models.CharfieldField(max_length = 10, unique = True)

class Admin(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)

class Pricipal(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)

class Teacher(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)

class Student(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
Run Code Online (Sandbox Code Playgroud)

方法#2:在 User 模型中添加额外的用户类型属性

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    user = models.CharfieldField(max_length = 10, unique = True)
    is_superuser = models.BooleanField(default = False)
    is_staff = models.BooleanField(default = False)
    is_principal = models.BooleanField(default = False)
    is_teacher = models.BooleanField(default = False)
    is_student = models.BooleanField(default = False

'''
User table in DB:
user | is_superuser | is_staff | is_principal | is_teacher | is_student
'''
Run Code Online (Sandbox Code Playgroud)

我的想法:

在方法 #1 中,由于内置的​​ User 模型有两个字段,is_staff 和 is_superuser,是否可以像上面的示例一样将这些字段实现/更改为 SuperUser/Admin 表?这意味着当我创建一个 admin/superuser 时,我希望它在 Admin 表中添加一个新行,而不是添加一个新用户并在内置 User 模型中将用户的 is_superuser 和 is_staff 字段更新为 1。

在方法#2 中,问题在于具有不同访问权限的表直接连接到它。例如,薪金模型(学生用户无法访问)与用户模型(包含学生用户)有直接联系。

我希望我能够得到一些见解,以及适当有效的实施方式,以防止将来出现任何实施不便和错误。非常感谢。

Tim*_*ion 6

我认为您使用方法 2 走在正确的道路上。它更轻巧,更直接。

我不会为每个权限级别使用自定义的“类用户”模型。过于复杂,无法扩展,并且查询数量会成倍增加,对您的问题没有太大好处。不是您的 UML 模式,而是其内容必须保证您的权限要求

如果权限级别不是互斥的:

from django.db import models
from django.contrib.postgres.fields import ArrayField


class User(AbstractUser):
    ADMIN = 0
    PRINCIPLE = 1
    TEACHER = 2
    STUDENT = 3
    USER_LEVEL_CHOICES = (
        (ADMIN, "Admin"),
        (PRINCIPLE, "Principle"),
        (TEACHER, "Teacher"),
        (STUDENT, "Student"),
    )
    status = ArrayField(
        models.IntegerField(choices=USER_LEVEL_CHOICES, blank=True, default=STUDENT),
    )
Run Code Online (Sandbox Code Playgroud)

但是你需要有更广泛的反思。


我认为您在谈论两个不同的问题:多态性权限

  • 多态性

多态性是对象具有多种形式的能力。对于 Django 模型,可以通过多种策略来完成:OneToOneField-正如您提到的- 多表继承抽象模型代理模型

非常好的资源:这篇文章和关于模型继承的Django 文档

这个非常复杂的问题都是指:同一个实体的几种形式有多少相似或不同。以及哪些操作特别相似或不同(数据形状、查询、权限等)

  • 权限设计:

您可以在多种模式中进行选择

  • 面向模型的权限:用户被授予“添加”、“查看”、“编辑”或“删除”权限Model这是在 Django 中使用内置Permission模型完成的,它有一个外键ContentType
  • 面向对象的权限:授予用户对每个Model 实例的“添加”、“查看”、“编辑”或“删除”权限django-guardian例如,一些软件包提供了这种能力。
  • 面向规则的权限:通过自定义逻辑而不是 M2M 表授予用户对模型实例的权限。djangorules包提供了这种架构。

  • 在方法 #2 中,如果您想在执行某些操作之前检查用户权限,则可以轻松检查 `request.user.status` 。根据该值,您是否引发错误,您是否执行操作,... (2认同)

kia*_*kia 2

我在几个项目中使用的方法之一是这样的(伪代码):

class User(AbstractUser):
    ADMIN = 0
    PRINCIPLE = 1
    TEACHER = 2
    STUDENT = 3
    USER_LEVEL_CHOICES = (
        (ADMIN, "Admin"),
        (PRINCIPLE, "Principle"),
        (TEACHER, "Teacher"),
        (STUDENT, "Student"),
    )
    user_level = models.IntgerField(choices=USER_LEVEL_CHOICES)


def lvl_decorator():
  def check_lvl(func):
    def function_wrapper(self, actor, action_on, *args, **kwargs):
        if 'action_lvl' not in action_on: # then action_on is user
            if actor.user_lvl < action_on.user_lvl:
                return True
            return False
        else: # then action_on is action of some kind for that user (you can add action_lvl to ... and pas them to this wapper)
            if actor.user_lvl < action_on.action_lvl:
                return True
            return False
    return function_wrapper
  return check_lvl
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此逻辑为任何操作编写包装函数,检查操作级别是否大于用户级别,例如:如果有人想要更改超级用户密码,他/她应该使用 0 级用户登录,但要更改普通用户的密码他/她应该是级别 0、1。此逻辑也可以应用于类、函数等操作。

创建基类,然后添加lvl_decorator到它然后从它固有的 => 这使您的代码超级干净并防止进一步复制粘贴。

我的意思的例子:

def lvl_decorator():
    def check_lvl(func):
        def function_wrapper(self, actor, action_on, *args, **kwargs):
            if 'action_lvl' not in action_on:  # then action_on is user
                if actor.user_lvl < action_on.user_lvl:
                    return True
                return False
            else:
                if actor.user_lvl < action_on.action_lvl:
                    return True
                return False

        return function_wrapper

    return check_lvl


class BaseClass(type):
    def __new__(cls, name, bases, local):
        for attr in local:
            value = local[attr]
            if callable(value):
                local[attr] = lvl_decorator()
        return type.__new__(cls, name, bases, local)


# in other locations like views.py use this sample
class FooViewDjango(object, ApiView): # don't remove object or this won't work, you can use any Django stuff you need to inherent.
    __metaclass__ = BaseClass

    def baz(self):
        print('hora hora')
Run Code Online (Sandbox Code Playgroud)

在任何你想要的地方使用这个基类。