Django自定义用户注册异常:无用户却提示邮箱已存在
自定义用户注册时的邮箱重复验证问题
我基于Django创建了Provider和Seeker两种自定义用户类型,编写注册视图后发现:添加Provider用户时,明明数据库里没有任何用户,却始终提示邮箱已存在。
我试过两种解决思路,但都没解决问题:
- 移除ProviderSerializer中的UniqueValidator,在视图里自行实现重复邮箱检测逻辑
- 先将用户保存到数据库,再检查是否存在重复邮箱
相关代码
注册视图代码
from django.shortcuts import render from rest_framework import status from rest_framework.response import Response from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.permissions import AllowAny, IsAdminUser from .models import Provider, Seeker from .serializer import ProviderSerializer from django.contrib.auth.models import Group from rest_framework import serializers @api_view(['POST']) @permission_classes([AllowAny]) def Register(request): if request.data.get('work_email'): seri = ProviderSerializer(data=request.data) if seri.is_valid(): try: user = seri.save() try: group = Group.objects.get(name='Provider') user.groups.add(group) except Group.DoesNotExist: return Response({"Message": "Exception at group assigning"}) except serializers.ValidationError as e: return Response(e.errors, status=status.HTTP_400_BAD_REQUEST) return Response({"Message": "User created successfully"}) else: return Response(seri.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response({"Message": "No Work_email"}, status=status.HTTP_400_BAD_REQUEST)
模型代码
from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class GenericUser(AbstractUser): username = models.CharField(max_length=150, blank=True,null = True) email = models.EmailField(max_length=255,unique=True,db_index=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS= [] class Provider(GenericUser): work_email = models.EmailField(max_length=255, unique=True, blank=False, null = True) class Seeker(GenericUser): ed_level = models.EmailField(max_length=255, blank=True)
问题根源
你的问题核心在于继承模型的字段唯一性验证逻辑错误:
- GenericUser的
email字段设置了全局唯一约束,所有用户(Provider/Seeker)的邮箱都在父表中唯一 - ProviderSerializer默认会继承父类的
email字段,并自动生成针对Provider.objects的UniqueValidator,但实际上邮箱的唯一性需要在整个GenericUser范围内检查,而非仅Provider用户 - 另外,默认的ModelSerializer创建用户时不会调用
create_user方法,会导致密码明文存储,这也是潜在问题
解决方案
方案1:修正序列化器的验证逻辑
重写ProviderSerializer,为email字段指定正确的全局查询集,同时处理密码哈希:
from rest_framework import serializers from .models import Provider, GenericUser from django.contrib.auth.hashers import make_password class ProviderSerializer(serializers.ModelSerializer): email = serializers.EmailField( validators=[ serializers.UniqueValidator( queryset=GenericUser.objects.all(), message="该邮箱已被注册" ) ] ) work_email = serializers.EmailField( validators=[ serializers.UniqueValidator( queryset=Provider.objects.all(), message="该工作邮箱已被注册" ) ] ) class Meta: model = Provider fields = ('email', 'password', 'work_email', 'first_name', 'last_name') # 根据需求添加字段 extra_kwargs = { 'password': {'write_only': True} } def create(self, validated_data): # 密码哈希处理 validated_data['password'] = make_password(validated_data['password']) return super().create(validated_data)
方案2:在视图中手动处理唯一性验证(优化你尝试的思路)
移除序列化器中的UniqueValidator,在视图中直接检查全局邮箱和工作邮箱的唯一性:
序列化器修改:
from rest_framework import serializers from .models import Provider from django.contrib.auth.hashers import make_password class ProviderSerializer(serializers.ModelSerializer): class Meta: model = Provider fields = ('email', 'password', 'work_email', 'first_name', 'last_name') extra_kwargs = { 'password': {'write_only': True} } def create(self, validated_data): validated_data['password'] = make_password(validated_data['password']) return super().create(validated_data)
视图修改:
from django.shortcuts import render from rest_framework import status from rest_framework.response import Response from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from .models import Provider, GenericUser from .serializer import ProviderSerializer from django.contrib.auth.models import Group @api_view(['POST']) @permission_classes([AllowAny]) def Register(request): work_email = request.data.get('work_email') email = request.data.get('email') if not work_email: return Response({"Message": "缺少工作邮箱"}, status=status.HTTP_400_BAD_REQUEST) # 检查全局邮箱是否已存在 if GenericUser.objects.filter(email=email).exists(): return Response({"email": ["该邮箱已被注册"]}, status=status.HTTP_400_BAD_REQUEST) # 检查工作邮箱是否已存在 if Provider.objects.filter(work_email=work_email).exists(): return Response({"work_email": ["该工作邮箱已被注册"]}, status=status.HTTP_400_BAD_REQUEST) seri = ProviderSerializer(data=request.data) if seri.is_valid(raise_exception=True): user = seri.save() try: group = Group.objects.get(name='Provider') user.groups.add(group) except Group.DoesNotExist: return Response({"Message": "分配用户组失败:Provider组不存在"}, status=status.HTTP_400_BAD_REQUEST) return Response({"Message": "用户创建成功"}, status=status.HTTP_201_CREATED)
额外注意事项
- 确保数据库已正确迁移:执行
python manage.py makemigrations和python manage.py migrate,保证GenericUser和Provider表的唯一索引正确创建 - 测试时使用不同的邮箱和工作邮箱,避免字段值重复导致的验证错误
- 如果使用SQLite,注意删除旧数据库文件后重新迁移,确保测试环境干净
内容的提问来源于stack exchange,提问作者Yaghi
相关产品推荐
相关产品推荐

