Как создать множество пользовательских профилей, которые наследуются от AbstractUser в Django? - PullRequest
1 голос
/ 25 апреля 2019
Оригинальный вопрос

Я хочу создать 3 UserProfile для 3 типов пользователей. Сначала я сделал CharFiled в качестве отметки, чтобы различать их, но потом я понял, что мне нужно добавить ForeignKey, чтобы связать этих пользователей. Что касается этого шага, я не знаю, как его осуществить.


Чтобы конкретизировать:

  • 3 типа пользователей, которые наследуют от AbstractUser, каждый из них может войти в систему
  • Среди них есть иерархия, очень похожая на:
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)

Ответы [ 2 ]

0 голосов
/ 25 апреля 2019

Вы можете использовать общие отношения Django , настроив модель 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
0 голосов
/ 25 апреля 2019

Это действительно зависит от того, чего вы пытаетесь достичь, но самый простой подход будет аналогичен полям is_staff и is_superuser в стандартной модели User.

class UserProfile(AbstractUser):
    is_type1_user = models.BooleanField(default=False)
    is_type2_user = models.BooleanField(default=False)
    is_type3_user = models.BooleanField(default=False)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...