如何自定义 django rest auth 密码重置电子邮件内容/模板

Posted

技术标签:

【中文标题】如何自定义 django rest auth 密码重置电子邮件内容/模板【英文标题】:How to customize django rest auth password reset email content/template 【发布时间】:2016-04-26 04:02:41 【问题描述】:

在 django rest_auth 密码重置中,默认电子邮件内容如下所示:-

您收到这封电子邮件是因为您请求在 localhost:8000 为您的用户帐户重置密码。

请到以下页面选择新密码:

http://localhost:8000/api/reset/Kih/89a-23809182347689312b123/

您的用户名,以防您忘记了:test

感谢您使用我们的网站!

localhost:8000 团队

如何自定义这封邮件的内容?

【问题讨论】:

【参考方案1】:

我最近需要在我的一个项目中实施同样的事情,但在任何地方都找不到彻底的答案。

所以我将我的解决方案留给将来需要它的任何人。

扩展 mariodev 的建议:

1。继承 PasswordResetSerializer 并覆盖 save 方法。

yourproject_app/serializers.py

from django.conf import settings
from rest_auth.serializers import PasswordResetSerializer as _PasswordResetSerializer

class PasswordResetSerializer(_PasswordResetSerializer):
    def save(self):
        request = self.context.get('request')
        opts = 
            'use_https': request.is_secure(),
            'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),

            ###### USE YOUR TEXT FILE ######
            'email_template_name': 'example_message.txt',

            'request': request,
        
        self.reset_form.save(**opts)

2。配置AUTH_USER_MODEL

yourproject/settings.py

###### USE YOUR USER MODEL ######
AUTH_USER_MODEL = 'yourproject_app.ExampleUser'

3。连接自定义PasswordResetSerializer 覆盖默认值

yourproject/settings.py

REST_AUTH_SERIALIZERS = 
    'PASSWORD_RESET_SERIALIZER': 
        'yourproject_app.serializers.PasswordResetSerializer',

4。将自定义电子邮件文本文件所在目录的路径添加到TEMPLATES

yourproject/settings.py

TEMPLATES = [
    
        ...
        'DIRS': [os.path.join(BASE_DIR, 'yourproject/templates')],
        ...
    
]

5。编写自定义电子邮件消息(默认从 Django 复制)

yourproject/templates/example_message.txt

% load i18n %% autoescape off %
% blocktrans %You're receiving this email because you requested a password reset 
for your user account at  site_name .% endblocktrans %

% trans "Please go to the following page and choose a new password:" %
% block reset_link %
 protocol :// domain % url 'password_reset_confirm' uidb64=uid token=token %
% endblock %
% trans "Your username, in case you've forgotten:" %  user.get_username 

% trans "Thanks for using our site!" %

% blocktrans %The  site_name  team% endblocktrans %

% endautoescape %

更新:此解决方案是为旧版本的 django-rest-auth (v0.6.0) 编写的。正如我从 cmets 中得知的那样,似乎对源包进行了一些更新,可以更轻松地处理开箱即用的自定义电子邮件模板。使用包中定义的方法总是比在我的解决方案中覆盖它们更好。虽然曾经是必需品,但可能不再如此。

【讨论】:

您的解决方案对我不起作用:我按照您的步骤完成了所有操作,但 rest-auth 仍使用其默认电子邮件模板 :( 我已将电子邮件放在 project/templates/emails/password-reset-email.html 下并覆盖 get_email_options(self)现在返回'html_email_template_name': 'templates/emails/password-reset-email.html',但仍然没有变化......我使用了我的自定义密码序列化程序,我使用pycharm调试器验证了这一点。所以设置应该是正确的,并且我的覆盖get_email_options也被调用了。有什么想法吗?谢谢!跨度> @ElectRocnic 而不是'templates/emails/password-reset-email.html' 尝试'password-reset-email.html',并确保您的模板目录在settings.py 中指定。另外,当我最初写这篇文章时,get_email_options(self) 不包含在 rest_auth 包中。我的方法需要覆盖save(self) 谢谢,尽管使用当前版本的 rest_auth,您只需要一点序列化程序代码。我将来可能会发布我的解决方案。 我仍然无法真正找到当前的解决方案。这真的很令人惊讶,因为这一定是一个非常常见的用例。 同样,我经历了很多例子,除了这个似乎对我有用。【参考方案2】:

您可以继承PasswordResetSerializer 并覆盖get_email_options 方法。例如:

from rest_auth.serializers import PasswordResetSerializer


class CustomPasswordResetSerializer(PasswordResetSerializer):
    def get_email_options(self):
        return 
            'subject_template_name': 'registration/password_reset_subject.txt',
            'email_template_name': 'registration/password_reset_message.txt',
            'html_email_template_name': 'registration/'
                                    'password_reset_message.html',
            'extra_email_context': 
                'pass_reset_obj': self.your_extra_reset_obj
            
        

【讨论】:

这对我有用。请注意,self.pass_reset_obj 仅用于示例目的。 这是迄今为止最好、最短、最新的解决方案。 您从哪里获得传递给 CustomPasswordResetSerializer 的 PasswordResetSerializer? @Efosa 我刚刚在我的 sn-p 中添加了导入 @M.Void,我喜欢这个解决方案,但我不明白如何触发它。我想在创建新用户时发送一个激活链接,通过点击激活链接用户可以重置他们的密码。【参考方案3】:

您需要使用自定义的save 方法连接您自己的重置密码序列化程序 (PASSWORD_RESET_SERIALIZER)。

(参考:https://github.com/Tivix/django-rest-auth/blob/v0.6.0/rest_auth/serializers.py#L123)

不幸的是,由于电子邮件选项的使用方式,您需要覆盖整个保存方法。我们将在下一个版本 (0.7.0) 中使其更加灵活

【讨论】:

【参考方案4】:

一个简单的解决方案是 创建模板目录:

-templates
   -registration
       password_reset_email.html

包含您想要的内容。 Django rest-auth 使用 django.contrib.auth 模板。

【讨论】:

我尝试了这个解决方案,它会覆盖但它并没有像应有的那样显示它,而是将代码的电子邮件返回给我。我不知道为什么。 ***.com/questions/60026522/… 这个答案有效,但遗憾的是,这是“password_reset_email.html”在明文模板中传递的。 @OpeyemiOdedeyi 这就是为什么您会将 html 代码视为文本的原因。您必须像 Biran K 提到的那样覆盖 PasswordResetSerializer。【参考方案5】:

如果您想使用 html 电子邮件模板,Brian 的答案的更新是添加

'html_email_template_name': 'account/email/example_message.html',

就在下面

###### USE YOUR TEXT FILE ###### 'email_template_name': 'account/email/example_message.txt',

这样您就可以使用 html 模板发送电子邮件

您可以通过检查 PasswordResetForm 类的 send_mail 方法来了解为什么会发生这种情况

class PasswordResetForm(forms.Form):
     email = forms.EmailField(label=_("Email"), max_length=254)

     def send_mail(self, subject_template_name, email_template_name,
              context, from_email, to_email, html_email_template_name=None):
              """
               Send a django.core.mail.EmailMultiAlternatives to `to_email`.
              """
             subject = loader.render_to_string(subject_template_name, context)
             # Email subject *must not* contain newlines
             subject = ''.join(subject.splitlines())
             body = loader.render_to_string(email_template_name, context)

             email_message = EmailMultiAlternatives(subject, body, from_email, [to_email])
             if html_email_template_name is not None:
                 html_email = loader.render_to_string(html_email_template_name, context)
                 email_message.attach_alternative(html_email, 'text/html')

             email_message.send()```

【讨论】:

【参考方案6】:

在您的模板文件夹中创建路径如下的目录

模板/管理员/注册/

现在将 django/contrib/admin/templates/registration/ 中的所有文件复制到您刚刚创建的目录中。您可以在安装 django 的位置找到此目录。在linux中,可以在这里找到

/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/registration

您需要 root 权限才能访问它。

现在,当您发送电子邮件时,将使用您刚刚复制到项目中的模板。

【讨论】:

不工作。我需要任何其他配置才能使其正常工作吗? 您是否遇到任何错误或仍在使用以前的模板? 仍在使用以前的模板。我需要配置新复制的模板路径吗?我已将 django/contrib/admin/templates/registration/ 的内容复制到 templates/admin/registration/。对吗? 没错。您在 urls.py 中忘记密码的网址是什么? url(r'^rest-auth/', include(rest_auth_urls)),【参考方案7】:

此链接可能会有所帮助。有了它,我能够找到电子邮件模板的位置以及如何自定义它们。

您可以在页面底部的下方找到信息 自定义电子邮件 http://www.sarahhagstrom.com/2013/09/the-missing-django-allauth-tutorial/#Customize_the_email_message

【讨论】:

只是添加@Ryan113 答案,django-rest-auth 版本 +0.9.5 使用 django-allauth 电子邮件消息和模板,因此无需从 django-rest-auth 中查找电子邮件模板,覆盖来自 django-allauth 的模板,如 ***.com/a/51685556/6586065 所述【参考方案8】:

所以对于dj-rest-auth,我就是这样做的:

from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.urls.base import reverse
from allauth.account import app_settings
from allauth.account.adapter import get_adapter
from allauth.account.utils import user_pk_to_url_str, user_username
from allauth.utils import build_absolute_uri
from dj_rest_auth.forms import AllAuthPasswordResetForm
from dj_rest_auth.serializers import PasswordResetSerializer

class CustomAllAuthPasswordResetForm(AllAuthPasswordResetForm):
    def save(self, request, **kwargs):
        current_site = get_current_site(request)
        email = self.cleaned_data['email']
        token_generator = kwargs.get('token_generator',
                                     default_token_generator)

        for user in self.users:

            temp_key = token_generator.make_token(user)

            # save it to the password reset model
            # password_reset = PasswordReset(user=user, temp_key=temp_key)
            # password_reset.save()

            # send the password reset email
            path = reverse(
                'password_reset_confirm',
                args=[user_pk_to_url_str(user), temp_key],
            )
            url = build_absolute_uri(None, path) # PASS NONE INSTEAD OF REQUEST

            context = 
                'current_site': current_site,
                'user': user,
                'password_reset_url': url,
                'request': request,
            
            if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
                context['username'] = user_username(user)
            get_adapter(request).send_mail('account/email/password_reset_key',
                                           email, context)
        return self.cleaned_data['email']

class CustomPasswordResetSerializer(PasswordResetSerializer):
    @property
    def password_reset_form_class(self):
        return CustomAllAuthPasswordResetForm

# settings.py
REST_AUTH_SERIALIZERS = 
    'PASSWORD_RESET_SERIALIZER':
    'api.users.api.serializers.CustomPasswordResetSerializer',

通过将None 传递给build_absolute_uri 而不是原来的request,它将采用django.contrib.sites 模块和SITE_ID=1 中的值。因此,您在 Django 管理员中定义为域的任何内容现在都将是重置 URL 中的域。如果您想让密码重置 URL 指向您的前端,这可能是有道理的,这可能是在不同域上运行的 React 应用程序。

我知道,拥有所有这些代码是非常可怕的,因为你只想更改重置 url 的域,但显然 all-auth / dj-rest 的开发人员并不关心这个简单的功能。

我真的希望他们实现一个简单的功能,我们可以在某个时候定义所有与身份验证相关的 URL 的域发送给用户。

【讨论】:

这成功了!非常感谢。

以上是关于如何自定义 django rest auth 密码重置电子邮件内容/模板的主要内容,如果未能解决你的问题,请参考以下文章

django-rest-auth 自定义注册无法保存额外字段

如何在 DRF + django-rest-auth 中使用自定义用户模型保存注册时的额外字段

使用 Django-rest-auth 创建新用户时创建自定义用户模型

当他从所有浏览器(Django-rest-auth,JWT)更改密码时如何注销用户?

如何使用 django-rest-auth 和 Mailgun 从 Django 发送密码重置电子邮件

如何在 django rest 框架中添加自定义权限和角色