Django 1.7.8无法发送重置密码邮件

16

项目中与urls.py相关的部分:

from django.conf.urls import include, url, patterns
urlpatterns = patterns('',

  # other ones ...

  url(r'^accounts/password/reset/$', 
  'django.contrib.auth.views.password_reset',
 {'post_reset_redirect' : '/accounts/password/reset/done/'}),

  url(r'^accounts/password/reset/done/$', 
  'django.contrib.auth.views.password_reset_done'),

  url(r'^accounts/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', 
  'django.contrib.auth.views.password_reset_confirm',
 {'post_reset_redirect' : '/accounts/password/done/'}),

  url(r'^accounts/password/done/$', 
  'django.contrib.auth.views.password_reset_complete'),

)

根据请求,这是密码重置表格:

{% extends "site_base.html" %}

{% block title %}Reset Password{% endblock %}

{% block content %}
<p>Please specify your email address to receive instructions for resetting it.</p>

<form action="" method="post">
    <div style="display:none">
        <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
    </div>
     {{ form.email.errors }}
    <p><label for="id_email">E-mail address:</label> {{ form.email }} <input type="submit" value="Reset password" /></p>
</form>
{% endblock %}

但是,每当我访问/accounts/password/reset/页面并填写电子邮件后点击回车,页面立即重定向到/accounts/password/reset/done/,并且不会发送电子邮件。

我的相关settings.py变量:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'XXX@gmail.com'
EMAIL_HOST_PASSWORD = 'XXXXXX' 
EMAIL_PORT = 587

DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER

我知道电子邮件是可行的,因为我的使用django-registration-redux的注册流程无瑕疵地运行。

有什么想法吗?


你能展示一下你的视图负责发送电子邮件的例子吗? - Tanorix
尝试暂时切换到file backend(或者如果您在开发环境中,则为控制台后端)-这应该有助于显示问题是在视图还是电子邮件配置中(我知道您说django-registration-redux中的电子邮件正在工作,但无论如何都值得一试)。 - Alasdair
你什么时候展示你的电子邮件地址?因为我们可以从你展示给我们的URL看到,在第154行,该函数尝试发送一封电子邮件,所以在你的urls.py中,你必须在^accounts/password/reset/done/$之后指定电子邮件和电子邮件模板。 - Tanorix
请发布您的密码重置表单的 HTML。 - professorDante
@professorDante:已添加。 - lollercoaster
显示剩余2条评论
7个回答

14

我尝试重现您的情况,并面临以下情形:

  1. 邮件仅发送给活跃用户。未关联用户的电子邮件将不会收到任何电子邮件(显然)。
  2. 我在第270行表单保存方法中遇到了错误:email = loader.render_to_string(email_template_name, c):

在/accounts/password/reset/处发生 NoReverseMatch 错误,参数“()”和关键字参数“{'token':'42h-4e68c02f920d69a82fbf','uidb64': b'Mg'}”无法找到“password_reset_confirm”的反向。已尝试0个模式:[]

看起来您的urls.py文件中没有包含任何名为“password_reset_confirm”的网址。因此,您应该更改您的网址:

url(r'^accounts/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
  'django.contrib.auth.views.password_reset_confirm',
 {'post_reset_redirect': '/accounts/password/done/'},),

收件人:

url(r'^accounts/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
  'django.contrib.auth.views.password_reset_confirm',
 {'post_reset_redirect': '/accounts/password/done/'}, name='password_reset_confirm'),
如果您已经完美地设置了电子邮件配置,那么您应该不会遇到任何问题。如果仍然遇到此问题,请使用调试器检查它在哪里出现异常。
PS:我已经在django 1.7.8中进行了测试,并且模板位于:Python34\Lib\site-packages\django\contrib\admin\templates\registration。URL和视图与您在问题中编写的相同。

5
当您向^accounts/password/reset/$发出POST请求时,将运行django.contrib.auth.views.password_reset函数。这将使用默认的django.contrib.auth.forms.PasswordResetForm类来验证电子邮件。
您应该注意到,只有活动用户匹配提供的电子邮件并且具有可用密码的用户才会收到电子邮件。
请尝试在Django管理shell中使用以下函数:
from django.contrib.auth.models import get_user_model

def check_password_reset_cond(email):
     for u in get_user_model().objects.filter(email__iexact=email):
         assert u.is_active
         assert u.has_usable_password()

是的,这些断言没有问题。在较新版本的Django中,您需要使用from django.contrib.auth.models import User而不是get_user_model - lollercoaster
感谢您提出密码重置电子邮件所需的条件 - 我正在尝试编写密码重置工作流程的测试,但不知道has_usable_password检查。 - BSchlinker
1
是的,如果你使用create_user创建用户并且没有设置密码,那么他们就不会重置。请使用password = User.objects.make_random_password() - Apollo Data

4

你的 templates 文件夹里有邮件模板吗?

registration/password_reset_email.html
registration/password_reset_subject.txt

是的,我都有。 - lollercoaster

3

好的,最有可能是表单中的清理方法出现了错误,并且您没有捕获该错误。因此,您只会在不发送电子邮件的情况下进行重定向,并且不知道其中的原因。在clean()方法中抛出的任何验证错误都将显示在non_field_errors中。您需要在页面中添加{{form.non_field_errors}}以查看它们。请尝试以下操作:

<form method="post">{% csrf_token %}
    {{{form.non_field_errors}}
    {{ form.email.errors }}
<p><label for="id_email">E-mail address:</label> {{ form.email }} <input    type="submit" value="Reset password" /></p>
</form>

2

我建议一定要检查垃圾邮件文件夹。因为有时候一些电子邮件过滤器会对带有链接的纯文本邮件进行严厉处理。

我建议使用(带有)Python调试器的IDE,并借助pdb(Python调试器)遍历django.contrib.auth.form.PasswordResetForm.save和django.contrib.auth.views.password_reset,以查看哪些部分出现了问题。


1

domtes提供的答案解决了我的问题,但我想强调一下可能的原因:

如果您要重置的用户在重置之前当前(即重置之前)具有无效密码,则Django将仅静默不发送重置电子邮件!

在我的情况下,我在管理界面中意外地覆盖了密码字段(自定义用户模型+LastPass),此后一个用户就再也无法重置密码了。


0
对于 Mac 用户,特别是如果你在正常的终端之外还有 iTerm,并且可能以前已经在 .bash_profile 中设置了环境变量,请在 ~/.zshrc 中重新设置它们(如果你使用 iTerm 运行 Django 项目)。
export EMAIL_USER='email@gmail.com'
export EMAIL_PASS='password'

重新加载文件~/.zshrcsource ~/.zshrc,或者简单地关闭并重新打开iTerm

当然,之前你已经在电子邮件设置中启用了不太安全的选项。


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