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
相关产品推荐
相关产品推荐

