簡體   English   中英

Django:實現多個用戶級別/角色/類型

[英]Django: implement multiple user levels / roles / types

我使用 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)

方法 #2:在用戶 model 中添加額外的用戶類型屬性

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
'''

我的想法:

在方法 #1 中,由於內置用戶 model 有兩個字段,is_staff 和 is_superuser,是否可以像上面的示例一樣將字段實現/更改為 SuperUser/Admin 表? 這意味着當我創建一個管理員/超級用戶時,我希望它在 Admin 表中添加一個新行,而不是在內置用戶 model 中添加一個新用戶並將用戶的 is_superuser 和 is_staff 字段更新為 1。

在方法 #2 中,它的問題是具有不同訪問權限的表直接連接到它。 例如,Salary model(學生用戶無法訪問)與用戶 model(包含學生用戶)有直接鏈接。

我希望我能夠獲得一些見解以及適當有效的實施方式,以防止將來出現任何實施不便和錯誤。 非常感謝。

我認為您使用方法#2走在正確的道路上。 它更輕,更直接。

我不會為每個權限級別使用自定義的“類似用戶”model。 過於復雜,無法擴展和增加查詢數量,對您的問題沒有太大好處。 不是您的 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),
    )

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


我認為您在談論兩個獨立的問題:多態性權限

  • 多態性

多態性是 object 具有許多 forms 的能力。 對於 Django model,可以使用多種策略來完成: OneToOneField - 正如您所提到的 -多表 inheritance抽象模型代理模型

很好的資源:這篇文章和 Django 文檔關於model inheritance

這個很復雜的問題都是指:你的幾個forms同一個實體有多少是相似的,還是不同的。 以及哪些操作特別相似或不同(數據形狀、查詢、權限等)

  • 權限設計:

您可以在多種模式中進行選擇

  • 面向模型的權限:授予用戶對Model的“添加”、“查看”、“編輯”或“刪除”權限。 這是在 Django 中完成的,具有內置Permission model,它具有ContentType的 ForeignKey
  • 面向對象權限:授予用戶對每個Model實例的“添加”、“查看”、“編輯”或“刪除”權限。 一些包提供了這種能力,例如django-guardian
  • 面向規則的權限:通過自定義邏輯而不是 M2M 表授予用戶對 Model 實例的權限。 django rules package 提供了這種架構。

您可以從 AbstractUser (完整的用戶 model ,完整的字段,包括 is_superuser 和 is_staff)創建個人資料,然后,一旦您擁有個人資料,讓用戶有機會創建其他類型的個人資料(學生、教師或校長)可以有自己的功能。

例如,在您的 models.py

class Profiles(AbstractUser):
    date_of_birth = models.DateField(max_length=128, blank=True, null=True, default=None, verbose_name=_(u'Date of birth'))
    principle = models.OneToOneField(Principles, null=True, blank=True, verbose_name=_(u'Principles'), on_delete=models.CASCADE)
    teacher = models.OneToOneField(Teachers, null=True, blank=True, verbose_name=_(u'Teachers'), on_delete=models.CASCADE)        
    student = models.OneToOneField(Students, null=True, blank=True, verbose_name=_(u'Students'), on_delete=models.CASCADE)  

    class Meta:
        db_table = 'profiles'
        verbose_name = _('Profile')
        verbose_name_plural = _('Profiles')

對於 model 您可以添加 class 方法,例如

def is_teacher(self):
    if self.teacher:
        return True
    else:
        return False

然后,您的教師 model 可能看起來像這樣

class Teachers(models.Model):
    image = models.FileField(upload_to=UploadToPathAndRename(settings.TEACHERS_IMAGES_DIR), blank=True, null=True, verbose_name=_('Teacher logo'))
    name = models.CharField(blank=False, null=False, default=None, max_length=255, validators=[MaxLengthValidator(255)], verbose_name=_('Name'))
    street = models.CharField( max_length=128, blank=False, null=True, default=None, verbose_name=_('Street'))
    created_by = models.ForeignKey('Profiles', null=True, blank=True, on_delete=models.SET_NULL)

我在幾個項目中使用的方法之一是這樣的(偽代碼):

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

然后,您可以使用此邏輯編寫包裝器 function 以檢查任何操作級別是否大於用戶級別,例如:如果有人想更改超級用戶密碼,他/她應該使用 level-0-user 登錄,但要更改普通用戶的密碼他/她應該是0、1級。這個邏輯也可以應用於class、函數等動作。

創建基礎 class ,然后將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')

在您想要的任何地方使用此基礎 class。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM