我有一个应用程序,我需要两种用户类型(UserType1 和 UserType2)。我使用带有布尔标志的 AbstractBaseUser
模型,然后在用户类型对象上创建 OneToOne 字段返回给用户(此时没有添加额外数据)。
我有一个独特的要求,我想强制执行一个 UniqueConstraint
,其中给定的电子邮件只能有一种用户类型。
用于确定用户类型的布尔标志是 is_user_type_1
和 is_user_type_2
,模型定义如下:
from django.conf import settings
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ValidationError
from django.db import models
from django_extensions.db.models import TimeStampedModel
class UserManager(BaseUserManager):
def create_user(self, username, email, password=None, **kwargs):
if not username:
raise ValueError('The given username must be set')
user = self.model(
username=username,
email=self.normalize_email(email),
**kwargs
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password=None):
user = self.create_user(
username=username,
email=self.normalize_email(email),
password=password,
)
user.is_user_type_1 = False
user.is_user_type_2 = False
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator()
username = models.CharField(
max_length=150,
unique=True,
validators=[username_validator],
error_messages={
'unique': "A user with that username already exists.",
},
)
email = models.EmailField()
is_user_type_1 = models.BooleanField(default=False)
is_user_type_2 = models.BooleanField(default=False)
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
objects = UserManager()
class Meta:
constraints = [
models.UniqueConstraint(
fields=[
'email',
'is_user_type_1',
'is_user_type_2',
],
name='unique_email_by_type'
)
]
class UserType1(TimeStampedModel):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True,
related_name='user_type1',
)
class UserType2(TimeStampedModel):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True,
related_name='user_type2'
)
我根据 UniqueConstraint
、User.Meta
和 email
在 user_type_1
中使用 user_type_2
来满足特定要求。我试图在此处遵循 Django 文档:https://docs.djangoproject.com/en/3.1/ref/models/constraints/
为了添加更多上下文,我还为 UserType1
和 UserType2
提供了以下序列化程序,我只想公开用户类型的端点并隐藏 User
对象的管理:
from django.db import transaction
from rest_framework import serializers
from .models import User, Athlete, Trainer
class AbstractUserTypeSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.EmailField(source='user.email')
password = serializers.CharField(source='user.password', write_only=True)
@transaction.atomic
def create(self, validated_data):
user_data = validated_data.pop('user')
user_data['is_user_type_1'] = self._is_user_type_1()
user_data['is_user_type_2'] = self._is_user_type_2()
user = User.objects.create(
**user_data
)
user_type = self.Meta.model.objects.create(
user=user,
**validated_data
)
return user_type
@transaction.atomic
def update(self, instance, validated_data):
user = instance.user
updated_user_data = validated_data.pop('user')
user.email = updated_user_data.get('email', user.email)
user.save()
instance.user = user
instance.save()
return instance
def _is_user_type_1(self) -> bool:
return False
def _is_user_type_2(self) -> bool:
return False
@staticmethod
def _get_default_fields() -> tuple:
return (
'pk',
'username',
'email',
'password',
'created',
'modified',
)
@staticmethod
def _get_default_extra_kwargs() -> tuple:
return {
'pk': {
'read_only': True
}
}
class UserType1Serializer(AbstractUserTypeSerializer):
class Meta:
model = UserType1
fields = AbstractUserTypeSerializer._get_default_fields()
extra_kwargs = AbstractUserTypeSerializer._get_default_extra_kwargs()
def _is_user_type_1(self):
return True
def _is_user_type_2(self):
return False
class UserType2Serializer(AbstractUserTypeSerializer):
class Meta:
model = UserType2
fields = AbstractUserTypeSerializer._get_default_fields()
extra_kwargs = AbstractUserTypeSerializer._get_default_extra_kwargs()
def _is_user_type_1(self):
return False
def _is_user_type_2(self):
return True
用户模型已在我的设置中注册:
AUTH_USER_MODEL = 'user.User'
但是,UniqueConstraint
不起作用,我仍然能够创建两个用户,其中 email
、user_type_1
和 user_type_2
字段是相同的。我认为这与上面的嵌套类有关,但无法在我的测试或在线中找到原因。
对于解释为什么在创建对象时没有满足此约束的任何帮助将不胜感激!
答案 0 :(得分:0)
我不确定这是否是解决问题的最佳方法,但如果其他人遇到此问题,我确实找到了解决方案。我认为验证未运行的主要原因是嵌套模型。由于我们通过从 User
调用 UserType1
隐式地通过 UserType2
和 create_user
对象创建 UserManager
,因此 Django 不会自动在 {{ 1}}。
为了解决这个问题,我在上面的 save
类的 validate_unique
方法中显式调用了 create_user
,如下所示:
UserManager