如何在Django中创建从Abstract User继承的多个用户配置文件?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Django中创建从Abstract User继承的多个用户配置文件?相关的知识,希望对你有一定的参考价值。

Original Question

我想为3种类型的用户创建3个UserProfile。起初,我做了一个CharFiled作为区分它们的标记,但后来我意识到我必须放一些ForeignKey来关联这些用户。至于这一步,我不知道如何实现它。


具体来说:

  • AbstractUser继承的3种类型的用户,每个用户都可以登录系统
  • 它们之间有层次结构,很像:
AdminstratorA(not a superuser)
AdminstratorB
    |--TeacherA
    |--TeacherB
         |--StudentA
         |--StudentB|

以下是我的失败(简化版),使用Django2.1 python3.7

  • 遇到了问题,因为我只能在AUTH_USER_MODEL = 'users.UserProfile'设置一个mysite\settings.py
apps\users\model

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

class AdministratorUser(AbstractUser):
    pass


class TeacherUser(AbstractUser):
    administrator = models.ForeignKey(AdministratorUser, on_delete=models.CASCADE)


class StudentUser(AbstractUser):
    teacher = models.ForeignKey(TeacherUser, on_delete=models.CASCADE)
  • 我将主表和其他3个表设置为补码。基于此,我基于xadmin设计了我的管理站点,数据并不代表。我不知道。所以我尝试重新设计我的模型,这是我在顶部描述的计划。
apps\users\model

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


class UserProfile(AbstractUser):
    user_type_choices = (
        ('student', 'student'),
        ('teacher', 'teacher'),
        ('teacher', 'teacher'),
    )
    identity = models.CharField(choices=user_type_choices, max_length=100, default='student')


class AdministratorUser(models.Model):
    user_profile = models.OneToOneField(UserProfile, on_delete=models.CASCADE)


class TeacherUser(models.Model):
    user_profile = models.OneToOneField(UserProfile, on_delete=models.CASCADE)
    administrator = models.ForeignKey(AdministratorUser, on_delete=models.CASCADE)


class StudentUser(models.Model):
    user_profile = models.OneToOneField(UserProfile, on_delete=models.CASCADE)
    teacher = models.ForeignKey(TeacherUser, on_delete=models.CASCADE)
答案

这实际上取决于你想要实现的目标,但最简单的方法类似于标准is_staff模型中的is_superuserUser字段。

class UserProfile(AbstractUser):
    is_type1_user = models.BooleanField(default=False)
    is_type2_user = models.BooleanField(default=False)
    is_type3_user = models.BooleanField(default=False)
另一答案

你可以通过以下方式定制Django's generic relations模型以及User来利用UserManager

首先,您需要导入相关的东西:

from django.apps import apps

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation

from django.contrib.auth.models import AbstractUser, UserManager as OriginalUserManager

UserManager

class UserManager(OriginalUserManager):

    def _create_user(self, username, email, password, **extra_fields):
        """
        Create and save a user with the given username, email, and password.
        """
        if not username:
            raise ValueError('The given username must be set')

        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)

        # handle profile creation at user creation
        profile_model = User.TYPE_MODELS.get(user.user_type)
        profile_model = apps.get_model(app_label='app1', model_name=profile_model)

        if profile_model is None:
            raise Exception(
                "Profile model has not been set in User.TYPE_MODELS for user type {}".format(user.user_type))

        profile = profile_model.objects.create()

        user.content_type = ContentType.objects.get_for_model(profile_model)
        user.object_id = profile.id
        user.content_object = profile

        user.save(using=self._db)
        return user

User模型:

class User(AbstractUser):
    TYPE_1 = 0
    TYPE_2 = 1

    TYPE_CHOICES = (
        (TYPE_1, "Type 1"),
        (TYPE_2, "Type 2"),
    )

    TYPE_MODELS = dict((
        (TYPE_1, "ProfileType1"),
        (TYPE_2, "ProfileType2"),
    ))

    user_type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES, default=TYPE_1)

    # Below the mandatory fields for generic relation
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()

    objects = UserManager()

    class Meta:
        unique_together = ('content_type', 'object_id',)

    @property
    def profile(self):
        return self.content_object

然后您可以定义您的配置文件(例如):

class BaseProfileModel(models.Model):
    _user = GenericRelation(User)

    @property
    def user(self):
        ctype = ContentType.objects.get_for_model(self.__class__)
        try:
            event = User.objects.get(content_type__pk=ctype.id, object_id=self.id)
        except User.DoesNotExist:
            return None

        return event


class ProfileType1(BaseProfileModel):
    x = models.IntegerField(default=0)


class ProfileType2(BaseProfileModel):
    y = models.IntegerField(default=0)

现在您可以使用它们的方式如下:

使用默认用户类型创建用户:

user = User.objects.create_user("someusername")
user.profile --> will yield a ProfileType1 instance

创建具有特定用户类型的用户

user = User.objects.create_user("someothername", user_type=User.TYPE_2)
user.profile --> will yield a ProfileType2 instance

在这两种情况下,如果我们掌握了配置文件实例,我们可以通过以下方式获取用户:

profile.user --> will yield our original user instance

以上是关于如何在Django中创建从Abstract User继承的多个用户配置文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中创建从绿色到红色的热图?

如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画

如何在 EF 中创建从两个表到一个源的一对一映射?

我如何在表格中创建从一行到另一个仪表板的链接

如何在 Python 中创建从 Pub/Sub 到 GCS 的数据流管道

交互式地图,如何在 symfony 中创建从地图标记的弹出窗口到模板的链接