You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework哈希密码存储与邮件发送异常求助

问题解决:DRF Serializer自定义逻辑未执行、密码未哈希、邮件发送失败

核心问题分析

你的代码存在几个致命错误,导致自定义的create逻辑完全未执行,进而引发密码未哈希、邮件不发送等问题:

  1. create方法定义位置错误:将create写在了Meta类内部,DRF会忽略这个方法,转而使用默认的ModelSerializer.create(),这就是为什么你的打印、日志、密码哈希逻辑都没触发,但仍返回201状态码(默认方法创建了用户)。
  2. 密码字段未加入序列化字段:fields列表中未包含password,即使设置了extra_kwargs,请求中的密码也不会被解析到validated_data中,set_password相当于无效调用。
  3. 提前引用未初始化的对象:在创建CustomUser实例前就调用user.pk,会直接抛出NameError。
  4. send_mail参数及用法错误:
    • send_mail是直接发送邮件的函数,返回值是成功发送的邮件数量,不是可调用send()的邮件对象
    • recipient_list要求是列表类型,不能传单个字符串
    • message被定义为元组,会导致邮件内容格式错误
  5. 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)

额外验证步骤

  1. 邮件后端配置:确保在settings.py中配置了控制台邮件后端,以便查看发送的邮件内容:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
  1. 日志配置:确保settings.py中开启了INFO级别的日志输出,以便看到logger.info的内容。
  2. 密码验证:创建用户后,在Django admin或shell中检查CustomUser的password字段是否为哈希值(以pbkdf2_sha256$开头)。

内容的提问来源于stack exchange,提问作者aka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:45:36