Django -允许在大用户模型中重复用户的电子邮件,但在其扩展模型中是唯一的?
我正在Django中做一个项目,它有一个从AbstractBaseUser扩展的User模型;和2个模型从用户(客户和技术人员)扩展。在创建用户、客户或技术人员时,它的工作都很好。
然而,每当我用同一封电子邮件(因为我将电子邮件定义为用户名USERNAME_FIELD)将新用户注册为客户或技术人员(/API/customer/register/或/api/customer/register/)时,它都会返回“This email is already exists”。当然,我理解这一点,因为我在User模型中定义了用户名和电子邮件字段的UNIQUE约束。我希望客户在自己的模型中是唯一的,而不是在用户模型中,技术人员也是如此。就像当你注册Uber或DoorDash时,你要么想成为一名客户,要么想成为一名司机,或者你可以两者兼而有之。您可以使用相同的电子邮件地址来注册两者。但是,如果您已经以客户或司机的身份使用该电子邮件注册,则不能使用同一电子邮件地址再次注册
我已经试图调整围绕这一点,但没有工作到目前为止!我的问题是,解决这个问题的最佳方法是什么?我是否应该为客户和技术人员创建两个不同的表?如果有人知道,Uber或DoorDash或Lyft使用什么架构来解决这个问题?
非常感谢您的任何帮助!我会把我的www.example.com贴models.py这里给你们看。干杯!
import uuid
from typing import Any, Optional
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin,
)
from django.db import models
from rest_framework_simplejwt.tokens import RefreshToken
class UserManager(BaseUserManager): # type: ignore
"""UserManager class."""
# type: ignore
def create_user(self, username: str, email: str, password: Optional[str] = None) -> 'User':
"""Create and return a `User` with an email, username and password."""
if username is None:
raise TypeError('Users must have a username.')
if email is None:
raise TypeError('Users must have an email address.')
user = self.model(username=username, email=self.normalize_email(email))
user.set_password(password)
user.save()
return user
def create_customer(self, username: str, email: str, password: Optional[str] = None) -> 'Customer':
"""Create and return a `User` with an email, username and password."""
if username is None:
raise TypeError('Users must have a username.')
if email is None:
raise TypeError('Users must have an email address.')
user = user.model(username=username, email=self.normalize_email(email))
user.set_password(password)
user.save()
return user
"""def create_customer(self, username, email, password=None, **extra_fields):
user = self.create_user(username, email, password, **extra_fields)
Customer.objects.create(user=user)
return user"""
def create_technician(self, username, email, password=None, **extra_fields):
user = self.create_user(username, email, password, **extra_fields)
Technician.objects.create(user=user)
return user
def create_superuser(self, username: str, email: str, password: str) -> 'User': # type: ignore
"""Create and return a `User` with superuser (admin) permissions."""
if password is None:
raise TypeError('Superusers must have a password.')
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.is_active = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = models.CharField(db_index=True, max_length=255, unique=True)
email = models.EmailField(db_index=True, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
#bio = models.TextField(null=True)
full_name = models.CharField(max_length=20000, null=True)
birth_date = models.DateField(null=True)
is_customer = models.BooleanField(default=False)
is_technician = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
# Tells Django that the UserManager class defined above should manage
# objects of this type.
objects = UserManager()
def __str__(self) -> str:
"""Return a string representation of this `User`."""
string = self.email if self.email != '' else self.get_full_name()
return f'{self.id} {string}'
@property
def tokens(self) -> dict[str, str]:
"""Allow us to get a user's token by calling `user.tokens`."""
refresh = RefreshToken.for_user(self)
return {'refresh': str(refresh), 'access': str(refresh.access_token)}
def get_full_name(self) -> Optional[str]:
"""Return the full name of the user."""
return self.full_name
def get_short_name(self) -> str:
"""Return user username."""
return self.username
class Customer(models.Model):
user = models.OneToOneField(User, related_name="customer", on_delete=models.CASCADE, primary_key=True)
objects = UserManager()
class Technician(models.Model):
user = models.OneToOneField(User, related_name="technician", on_delete=models.CASCADE, primary_key=True)
字符串
我serializers.py
from django.contrib.auth import authenticate
from rest_framework import exceptions, serializers
from rest_framework_simplejwt.tokens import RefreshToken, TokenError
from .models import Customer, Technician
from django.contrib.auth import get_user_model
User = get_user_model()
from .utils import validate_email as email_is_valid
class RegistrationSerializer(serializers.ModelSerializer[User]):
"""Serializers registration requests and creates a new user."""
password = serializers.CharField(max_length=128, min_length=8, write_only=True)
class Meta:
model = User
fields = [
'email',
'username',
'password',
'full_name',
]
def validate_email(self, value: str) -> str:
"""Normalize and validate email address."""
valid, error_text = email_is_valid(value)
if not valid:
raise serializers.ValidationError(error_text)
try:
email_name, domain_part = value.strip().rsplit('@', 1)
except ValueError:
pass
else:
value = '@'.join([email_name, domain_part.lower()])
return value
def create(self, validated_data): # type: ignore
"""Return user after creation."""
user = User.objects.create_user(
username=validated_data['username'], email=validated_data['email'], password=validated_data['password']
)
user.full_name = validated_data.get('full_name', '')
user.save(update_fields=['full_name'])
return user
class CustomerRegistrationSerializer(serializers.ModelSerializer[User]):
"""Serializers registration requests and creates a new user."""
password = serializers.CharField(max_length=128, min_length=8, write_only=True)
class Meta:
model = User
fields = [
'email',
'username',
'password',
'full_name',
]
def validate_email(self, value: str) -> str:
"""Normalize and validate email address."""
valid, error_text = email_is_valid(value)
if not valid:
raise serializers.ValidationError(error_text)
try:
email_name, domain_part = value.strip().rsplit('@', 1)
except ValueError:
pass
else:
value = '@'.join([email_name, domain_part.lower()])
return value
def create(self, validated_data): # type: ignore
"""Return user after creation."""
customer = User.objects.create_customer(username=validated_data['username'], email=validated_data['email'], password=validated_data['password'])
customer.full_name = validated_data.get('full_name', '')
customer.is_customer = True
customer.save(update_fields=['full_name','is_customer'])
return customer
class TechnicianRegistrationSerializer(serializers.ModelSerializer[User]):
"""Serializers registration requests and creates a new user."""
password = serializers.CharField(max_length=128, min_length=8, write_only=True)
class Meta:
model = User
fields = [
'email',
'username',
'password',
'full_name',
]
def validate_email(self, value: str) -> str:
"""Normalize and validate email address."""
valid, error_text = email_is_valid(value)
if not valid:
raise serializers.ValidationError(error_text)
try:
email_name, domain_part = value.strip().rsplit('@', 1)
except ValueError:
pass
else:
value = '@'.join([email_name, domain_part.lower()])
return value
def create(self, validated_data): # type: ignore
"""Return user after creation."""
technician = User.objects.create_technician(username=validated_data['username'], email=validated_data['email'], password=validated_data['password'])
technician.full_name = validated_data.get('full_name', '')
technician.is_technician = True
technician.save(update_fields=['full_name','is_technician'])
print(technician.tokens)
return technician
型
我曾尝试调整创建功能或自定义用户名或电子邮件的客户和技术人员,但没有一个工程给我。
1条答案
按热度按时间bhmjp9jg1#
到目前为止,你所实现的几乎是Django对多表继承的实现。您需要将用户名移到扩展类中(并在代码中实际扩展它,而不仅仅是概念上的扩展)。
您可以在文档中阅读如何实现这一点。
你的代码应该看起来像这样:
字符串
django在这些模型上实现了一对一的字段,这提供了一些简洁的语法简化,但公平的警告,这可能会在进一步的实现中产生一些挑战。
至于什么是最好的解决方案,没有,只有一个最适合你的需求。最好不要想得太远,而是面对挑战。谁知道你最终可能会更喜欢这两个表系统。另一方面,共享某些属性的用户超类可能证明是有用的。
有关更多选项,请查看此