Django -允许在大用户模型中重复用户的电子邮件,但在其扩展模型中是唯一的?

cnh2zyt3  于 2023-08-08  发布在  Go
关注(0)|答案(1)|浏览(137)

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


我曾尝试调整创建功能或自定义用户名或电子邮件的客户和技术人员,但没有一个工程给我。

bhmjp9jg

bhmjp9jg1#

到目前为止,你所实现的几乎是Django对多表继承的实现。您需要将用户名移到扩展类中(并在代码中实际扩展它,而不仅仅是概念上的扩展)。
您可以在文档中阅读如何实现这一点。
你的代码应该看起来像这样:

class Customer(User):
    username = models.CharField(db_index=True, max_length=255, unique=True)

    objects = UserManager()

class Technician(User):
    username = models.CharField(db_index=True, max_length=255, unique=True)

字符串
django在这些模型上实现了一对一的字段,这提供了一些简洁的语法简化,但公平的警告,这可能会在进一步的实现中产生一些挑战。
至于什么是最好的解决方案,没有,只有一个最适合你的需求。最好不要想得太远,而是面对挑战。谁知道你最终可能会更喜欢这两个表系统。另一方面,共享某些属性的用户超类可能证明是有用的。
有关更多选项,请查看此

相关问题