如何以编程方式在 django 1.7.6 中触发密码重置电子邮件?

How to programmatically trigger password reset email in django 1.7.6?

我遇到了一个问题,我不得不将 200 多个新用户加载到我的 django 应用程序中,并立即向他们发送密码重置电子邮件。这必须只发生一次,只能由我和 运行 在后端悄悄地完成。网上冲浪只让我找到一个或多或少正确的答案:Trigger password reset email in django without browser?。唯一的问题是这个 post 大约有 4 年历史,当然当我尝试解决方案时,它没有用...

我提到的 link 中最有价值的两点:

  1. 在最新版本的 Django 中,我们需要调用 form.is_valid()
  2. 电子邮件发送在 save() 时完成。

以下是我如何查询我需要的用户并向他们每个人发送密码重置 link:

def find_users_and_send_email():
    from django.http import HttpRequest
    from django.contrib.auth.forms import PasswordResetForm
    from django.contrib.auth.models import User
    import logging
    logger = logging.getLogger(__name__)
    
    users = User.objects.filter(date_joined__gt = '2015-04-16')
    for user in users:
        try:
            if user.email:
                logger.info("Sending email for to this email:", user.email)
                form = PasswordResetForm({'email': user.email})
                
                assert form.is_valid()
                request = HttpRequest()
                request.META['SERVER_NAME'] = 'help.mydomain.com'
                request.META['SERVER_PORT'] = '443'
                form.save(
                    request= request,
                    use_https=True,
                    from_email="username@gmail.com", 
                        email_template_name='registration/password_reset_email.html')

        except Exception as e:
            logger.warning(str(e))
            continue

    return 'done'
  1. 通常 PasswordResetForm 使用来自前端的“请求”,但我没有。所以我简单地创建了一个。
  2. 当我按照 link 中的示例进行操作时,它失败了。它无法在请求中找到服务器名称。 (有道理,因为我凭空实例化了我的请求)
  3. 当我固定服务器名称时,它会询问服务器端口。由于我使用的是https,所以我的端口是443,否则使用默认端口。
  4. 如果您使用https,请不要忘记在保存表格时注明use_https=True

我发现将@chabislav 的回答包含在自定义管理命令中很有用,然后可以根据需要重复使用。请注意,我删除了用户对象过滤器,因为我知道我想向 all 用户发送密码重置指令。

# myproject/myapp/management/commands/send_password_reset_emails.py
from django.core.management.base import BaseCommand, CommandError
# I use a custom user model, so importing that rather than the standard django.contrib.auth.models
from users.models import User
from django.http import HttpRequest
from django.contrib.auth.forms import PasswordResetForm


class Command(BaseCommand):
    help = 'Emails password reset instructions to all users'

    def handle(self, *args, **kwargs):
        users = User.objects.all()
        for user in users:
            try:
                if user.email:
                    print("Sending email for to this email:", user.email)
                    form = PasswordResetForm({'email': user.email})
                    assert form.is_valid()
                    request = HttpRequest()
                    request.META['SERVER_NAME'] = 'www.example.com'
                    request.META['SERVER_PORT'] = 443
                    form.save(
                        request= request,
                        use_https=True,
                        from_email="admin@mysite.com", 
                        email_template_name='registration/password_reset_email.html'
                        )
            except Exception as e:
                print(e)
                continue

        return 'done'

然后可以 运行 使用 python manage.py send_password_reset_emails 轻松实现。

请注意,我收到一条错误消息 (550, b'5.7.1 Relaying denied'),这最终成为我的本地主机电子邮件服务器设置方式的问题,但一旦清除该错误消息,它就运行良好。将它放在管理命令中也很好,因为它不会阻塞您定期使用的代码(视图)。