Django 2.0中出现关键字参数uidb64的NoReverseMatch错误

23

我不明白为什么我的代码不能运行。之前它是可以工作的,但现在当我运行服务器并测试时,代码却不能工作。

当用户注册时,我会发送一封激活邮件给他,内容如下:

def send_activation_email(serializer, request, user):
    current_site = get_current_site(request)
    message = render_to_string('acc_active_email.html', {
        'user': user,
        'domain': current_site.domain,
        'uid': urlsafe_base64_encode(force_bytes(user.pk)),
        'token': account_activation_token.make_token(user),
    })
    mail_subject = 'Activate your blog account.'
    to_email = serializer.data['email']

    email = EmailMessage(mail_subject, message, to=[to_email])
    email.send()

激活电子邮件模板.html

{% autoescape off %}
Hi {{ user.username }},
Please click on the link to confirm your registration,

http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
{% endautoescape %}

和我的URL文件

.
.
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        views.activate_account, name='activate'),
.
.

但是我遇到了这个错误:

Exception Type:     NoReverseMatch
Exception Value:    

Reverse for 'activate' with keyword arguments '{'uidb64': b'NDM', 'token': '4qz-8f770502bd8b02786da9'}' not found. 1 pattern(s) tried: ['activate/(?P<uidb64>[0-9A-Za-z_\\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$']

突出显示这一行:http://{{ domain }}{% url 'activate' uidb64=uid token=token %}


我在想它是否无法工作是因为uid是字节。尝试硬编码'uid': 'NDM'来查看是否可以修复错误。 - Alasdair
@Alasdair 哇,如果我在send_activation_email方法中设置'uid': 'NDM',它就可以工作了。这很奇怪。 - r2546971
好的,看起来你需要将字节转换为字符串才能反转URL。 - Alasdair
4个回答

56

在 Django 2.0 和 2.1 中,您应该在将 uid 进行 base64 编码后调用 decode() 方法将其转换为字符串:

message = render_to_string('acc_active_email.html', {
    'user': user,
    'domain': current_site.domain,
    'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
    'token': account_activation_token.make_token(user),
})
请查看Django 2.0 发布说明中的注释获取更多信息。在 Django 2.2+ 中,urlsafe_base64_encode返回字符串,因此无需解码。
message = render_to_string('acc_active_email.html', {
    'user': user,
    'domain': current_site.domain,
    'uid': urlsafe_base64_encode(force_bytes(user.pk)),
    'token': account_activation_token.make_token(user),
})

通过使用force_text, 应该可以编写与Django <= 1.11, 2.0-2.1和2.2+兼容的代码。请注意,以下内容未经测试。

from django.utils.encoding import force_text

message = render_to_string('acc_active_email.html', {
    'user': user,
    'domain': current_site.domain,
    'uid': force_text(urlsafe_base64_encode(force_bytes(user.pk))),
    'token': account_activation_token.make_token(user),
})

一旦您不再支持Django < 2.2,您可以放弃使用force_text并使用第二个代码片段。


6

对于较新版本的Django,您可以使用slug语法。例如:

path(
    'activate/<slug:uidb64>/<slug:token>/',
    views.activate_account, 
    name='activate'
)

1
在我的情况下,我将我的模板配置(acc_active_email.html)如下所示:
{% autoescape off %}

Hello {{ name }}!

Please confirm your email by clicking on the following link.

http://{{ domain }}/activate/{{ uid }}/{{ token }}

{% endautoescape %}

接下来,我在我的应用的urls.py中添加了以下内容:

path('activate/<uidb64>/<token>/', views.activate, name='activate')

我不知道这是否是最好的方法,但它解决了我的问题。然而,我使用的是django 4.1版本,所以对你可能不起作用。


0
也许这很愚蠢,因为URL不是动态的,但它起作用了...
path('activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', views.activate, name='activate'),

2
请详细阐述您的答案,以便更容易理解。 - kennysliding

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接