Django REST Framework哈希密码存储与邮件发送异常求助
问题解决:DRF Serializer自定义逻辑未执行、密码未哈希、邮件发送失败
核心问题分析
你的代码存在几个致命错误,导致自定义的create逻辑完全未执行,进而引发密码未哈希、邮件不发送等问题:
create方法定义位置错误:将create写在了Meta类内部,DRF会忽略这个方法,转而使用默认的ModelSerializer.create(),这就是为什么你的打印、日志、密码哈希逻辑都没触发,但仍返回201状态码(默认方法创建了用户)。- 密码字段未加入序列化字段:
fields列表中未包含password,即使设置了extra_kwargs,请求中的密码也不会被解析到validated_data中,set_password相当于无效调用。 - 提前引用未初始化的对象:在创建
CustomUser实例前就调用user.pk,会直接抛出NameError。 send_mail参数及用法错误:send_mail是直接发送邮件的函数,返回值是成功发送的邮件数量,不是可调用send()的邮件对象recipient_list要求是列表类型,不能传单个字符串message被定义为元组,会导致邮件内容格式错误
- City/District处理逻辑矛盾:先
pop了city和district,随后又在CustomUser实例化中引用validated_data["city"],会触发KeyError。
修复后的完整代码
Serializer代码
from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from django.core.mail import send_mail import traceback import logging logger = logging.getLogger(__name__) class UserSerializer(serializers.ModelSerializer): class Meta: model = CustomUser fields = [ "first_name", "last_name", "email", "phone_number", "address", "zip_code", "company_name", "gender", "birthdate", "referral_code", "city", "district", "password", # 新增密码字段 ] read_only_fields = [ "is_active", "is_staff", "is_superuser", "is_email_verified", "is_address_verified", "is_company_verified", "created_at", "updated_at", "login_attempts", ] extra_kwargs = {"password": {"write_only": True}} # 将create方法移到Meta类外部,与Meta同级 def create(self, validated_data): BASE_URL = "http://127.0.0.1:8000/api/p/users/" sender_mail = "313delivery@gmail.com" mail_subject = "Activation link has been sent to your email id" # 先处理关联的City和District对象 city = City.objects.get(pk=validated_data.pop("city")) district = District.objects.get(pk=validated_data.pop("district")) # 创建用户实例并设置哈希密码 user = CustomUser.objects.create( first_name=validated_data["first_name"], last_name=validated_data["last_name"], email=validated_data["email"], phone_number=validated_data["phone_number"], zip_code=validated_data["zip_code"], company_name=validated_data["company_name"], gender=validated_data["gender"], birthdate=validated_data["birthdate"], referral_code=validated_data["referral_code"], city=city, # 使用已查询的对象 district=district, ) user.set_password(validated_data["password"]) # 生成激活令牌(此时user已创建,pk可用) UID = urlsafe_base64_encode(force_bytes(user.pk)) TOKEN = account_activation_token.make_token(user) activation_link = f"{BASE_URL}activate/{UID}/{TOKEN}" # 调试输出 traceback.print_exc() print("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr") logger.info("before sending email ------") # 发送邮件 send_mail( subject=mail_subject, message=f"Your activation link: {activation_link}", from_email=sender_mail, recipient_list=[validated_data["email"]], # 改为列表 fail_silently=False, ) user.otp = TOKEN user.save() logger.info("after sending email ------") return user
View代码(无需修改,保持原逻辑即可)
class UserCreateView(APIView): def post(self, request): serializer = UserSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
额外验证步骤
- 邮件后端配置:确保在
settings.py中配置了控制台邮件后端,以便查看发送的邮件内容:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
- 日志配置:确保
settings.py中开启了INFO级别的日志输出,以便看到logger.info的内容。 - 密码验证:创建用户后,在Django admin或shell中检查
CustomUser的password字段是否为哈希值(以pbkdf2_sha256$开头)。
内容的提问来源于stack exchange,提问作者aka
相关产品推荐
相关产品推荐

