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

Django用户激活失败:TypeError需字节对象而非字符串

账号令牌激活无效及TypeError问题解决

问题描述

配置用户账号令牌激活机制后,点击邮件激活链接始终返回无效页面。调试时执行print(User.objects.get(pk=uid))抛出TypeError,提示“a bytes-like object is required, not 'str'”,该配置曾在其他项目正常运行。

相关代码

urls.py

# Activation
path('activate/<uidb64>/<token>/',
     user_views.activate, name='activate'),

views.py

User = get_user_model()  # Get custom user model

def activate(request, uidb64, token):
    # Debugging
    uid = force_bytes(urlsafe_base64_decode(uidb64))
    print(User.objects.get(pk=uid))
    try:
        uid = force_bytes(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.email_confirmed = True
        user.save()
        login(request, user)
        return redirect('/dashboard/overview/')
    else:
        return render(request, 'user/authentication/account_activation_invalid.html')

tokens.py

from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six

class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
                six.text_type(user.pk) + six.text_type(timestamp) +
                six.text_type(user.email_confirmed)
        )

account_activation_token = AccountActivationTokenGenerator()

models.py(自定义用户模型)

class UserProfile(AbstractBaseUser, PermissionsMixin):

    # Unique identifier
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    # Email and name of the user
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)

    # Privilege and security booleans
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    email_confirmed = models.BooleanField(default=False)

    objects = UserProfileManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send mail to user - Copied from original class"""
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def __str__(self):
        return self.email

问题原因

当前自定义用户模型使用UUID作为主键,而urlsafe_base64_decode(uidb64)返回的是bytes类型数据。直接用bytes类型的UUID去查询数据库会触发类型不匹配错误——UUID字段需要字符串类型的主键值,而非bytes。

之前的项目能正常运行,大概率是因为使用了默认的整数型主键,force_bytes转成的bytes可以直接被解析为整数,适配整数主键的查询逻辑。

解决方案

将解码后的bytes数据转换为字符串类型,使用Django提供的force_str(Django 3.0+)或force_text(旧版本Django)工具函数。

修改views.py中的激活函数:

from django.utils.encoding import force_str  # Django 3+ 用这个;旧版本用 force_text

User = get_user_model()

def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))  # 替换 force_bytes 为 force_str
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    # 后续逻辑保持不变
    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.email_confirmed = True
        user.save()
        login(request, user)
        return redirect('/dashboard/overview/')
    else:
        return render(request, 'user/authentication/account_activation_invalid.html')

额外说明

  • 生成激活链接时,需保持编码逻辑正确:使用urlsafe_base64_encode(force_bytes(user.pk))(编码时转bytes,解码时转回字符串)。
  • 如果使用Django 3.0及以上版本,six库已被逐步弃用,tokens.py中的six.text_type可替换为str,提升代码兼容性:
    class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
        def _make_hash_value(self, user, timestamp):
            return (
                    str(user.pk) + str(timestamp) +
                    str(user.email_confirmed)
            )
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:31:17