在我看来,django只支持纯文本消息,用于密码重置电子邮件的开箱即用。如何为此目的使用html模板?
答案 0 :(得分:6)
urls.py
url(r'^user/password/reset/$',
'YOUR_APP.views.password_reset',
{'post_reset_redirect' : '/#/login?resetemail=true'},
name="password_reset"),
views.py
from django.contrib.auth.views import password_reset as django_password_reset
from YOUR_APP.forms import CustomPasswordResetForm
def password_reset(*args, **kwargs):
"""
Overriding the Email Password Resert Forms Save to be able to send HTML email
"""
kwargs['password_reset_form'] = CustomPasswordResetForm
return django_password_reset(*args, **kwargs)
form.py
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.tokens import default_token_generator
class CustomPasswordResetForm(PasswordResetForm):
"""
Overriding the Email Password Resert Forms Save to be able to send HTML email
"""
def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
use_https=False, token_generator=default_token_generator, request=None, email_subject_name='registration/password_reset_subject.txt', **kwargs):
from django.core.mail import EmailMultiAlternatives
from django.utils.html import strip_tags
from django.template.loader import render_to_string
from django.contrib.sites.models import get_current_site
from django.utils.http import int_to_base36
for user in self.users_cache:
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
c = {
'email': user.email,
'domain': domain,
'site_name': site_name,
'uid': int_to_base36(user.id),
'user': user,
'token': token_generator.make_token(user),
'protocol': use_https and 'https' or 'http',
}
render = render_to_string(email_template_name, c)
render_subject = render_to_string(email_subject_name, c)
msg = EmailMultiAlternatives(render_subject, strip_tags(render), None, [user.email])
msg.attach_alternative(render, "text/html")
msg.send()
答案 1 :(得分:5)
您可以覆盖save
的{{1}}方法,并将新表单作为参数传递给django.contrib.auth.forms.PasswordResetForm
视图。
答案 2 :(得分:2)
password_reset_email.html
password_reset_email.txt
password_reset_subject.txt
django.contrib.auth.forms.PasswordResetForm
。PasswordResetForm.save()
的参数以指向您的自定义模板。PasswordResetForm.send_email()
。django.contrib.auth.views.PasswordResetView.form_class
以使用新的 PasswordResetForm
子类。password_reset_email.html
password_reset_email.txt
password_reset_subject.txt
password_reset_email.html
{% autoescape off %}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Password Reset</title>
<style type="text/css">
body {
background-color: #ffffff;
font-size: 14px;
line-height: 16px;
font-family: PTSansRegular,Arial,Helvetica,sans-serif;
height: 100%;
margin: 0;
padding: 0;
border: 0;
outline: 0;
}
a.button {
background-color: #007bff;
border-color: #007bff;
border-radius: 5px;
color: #ffffff;
cursor: pointer;
display: inline-block;
font-size: 15px;
line-height: 18px;
font-weight: bold;
font-family: PTSansRegular,Arial,Helvetica,sans-serif;
padding: 7px;
text-align: center;
text-decoration: none;
white-space: nowrap;
width: 150px;
}
.center {
text-align: center
}
.container {
min-height: 100%;
min-width: 650px;
position: relative;
width: 100%;
}
p {
text-align:left
}
table {
margin: auto;
width:650px;
}
td {
padding-right: 14px;
padding-left: 14px;
}
</style>
</head>
<body>
<div class="container">
<!-- BEGIN EMAIL -->
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<p>Hello {{ user.get_username }},</p>
<p>A request has been received to change the password for your account.</p>
<p class="center">
<a target="_blank" class="button"
href="{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}">
Reset Password
</a>
</p>
<p>This link can only be used once. If you need to reset your password again, please visit
<a href="{{ protocol }}://{{domain}}">{{ site_name }}</a> and request another reset.</p>
<p>If you did not make this request, please contact us immediately at
<a href="mailto: YOUR_SUPPORT_EMAIL">YOUR_SUPPORT_EMAIL</a>.</p>
<p>Sincerely,</p>
<p>The YOUR_COMPANY_NAME Team</p>
</td>
</tr>
</table>
<!-- END EMAIL -->
<table class="spacer">
<tr><td class="spacer"> </td></tr>
</table>
<!-- BEGIN FOOTER -->
<table align="center">
<tr>
<td>
<p class="center"><img src="cid:logo" /></p>
</td>
</tr>
<tr>
<td class="center">YOUR_ADDRESS_AND_OR_COPYRIGHT</td>
</tr>
</table>
<!-- END FOOTER -->
</div>
</body>
</html>
{% endautoescape %}
password_reset_email.txt
{% autoescape off %}
Hello {{ user.get_username }},
A request has been received to change the password for your account. Click the link below to reset your password.
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}">
This link can only be used once. If you need to reset your password again, please visit
{{ protocol }}://{{domain}}">{{ site_name }} and request another reset.
If you did not make this request, please contact us immediately at YOUR_SUPPORT_EMAIL.
Sincerely,
The YOUR_COMPANY_NAME Team
YOUR_COMPANY_NAME
YOUR_ADDRESS_AND_OR_COPYRIGHT
{% endautoescape %}
django.contrib.auth.forms.PasswordResetForm
django.contrib.auth.forms.PasswordResetForm
的子类并覆盖 save()
方法。class CustomPasswordResetForm(PasswordResetForm):
"""Override the default Django password-reset form to send the password reset
email using both HTML and plain text.
"""
def save(
self,
domain_override: Optional[str] = None,
subject_template_name: str = PASSWORD_RESET_SUBJECT_TEMPLATE,
email_template_name: str = PASSWORD_RESET_TEXT_TEMPLATE,
use_https: Optional[bool] = None,
token_generator: PasswordResetTokenGenerator = default_token_generator,
from_email: Optional[str] = FROM_EMAIL,
request: Optional[WSGIRequest] = None,
html_email_template_name: Optional[str] = PASSWORD_RESET_HTML_TEMPLATE,
extra_email_context: Optional[Dict[str, str]] = None
) -> None:
"""Generate a one-use only link for resetting password and email it to
the user.
Args:
domain_override: Optional; Domain name to use in the email message
template that overrides the actual domain from which the email is
sent. Defaults to None.
subject_template_name: Optional; Warning: this argument is overridden
by the global variable ``PASSWORD_RESET_SUBJECT_TEMPLATE``.
email_template_name: Optional; Warning: this argument is overridden by
the global variable ``PASSWORD_RESET_TEXT_TEMPLATE``.
use_https: Optional; If True, use HTTPS, otherwise use HTTP. Defaults
to False. Note that if the password reset HTTP request is received
via HTTPS, `use_https` will be set to True by the auth view.
token_generator: Optional; Strategy object used to generate and check
tokens for the password reset mechanism. Defaults to an instance
of ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
from_email: Optional; Warning: this argument is overridden by the
global variable``FROM_EMAIL``.
request: Optional; The HttpRequest object. Defaults to None.
html_email_template_name: Warning: this argument is overridden by the
global variable ``PASSWORD_RESET_HTML_TEMPLATE``.
extra_email_context: Optional; Key-value pairs to add to the context
dictionary used to render the password reset email templates.
Defaults to None.
"""
email_template_name = PASSWORD_RESET_TEXT_TEMPLATE
from_email = FROM_EMAIL
html_email_template_name = PASSWORD_RESET_HTML_TEMPLATE
subject_template_name = PASSWORD_RESET_SUBJECT_TEMPLATE
email = self.cleaned_data["email"]
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
UserModel = get_user_model()
email_field_name = UserModel.get_email_field_name() # type: ignore
for user in self.get_users(email):
user_email = getattr(user, email_field_name)
context = {
'email': user_email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
**(extra_email_context or {}),
}
self.send_mail(
subject_template_name = subject_template_name,
email_template_name = email_template_name,
context = context,
from_email = from_email,
to_email = user_email,
html_email_template_name = html_email_template_name
)
PasswordResetForm.save()
的参数以指向您的自定义模板。from typing import Final
# Constants for sending password-reset emails.
LOGO_FILE_PATH: Final[str] = "img/logo.png"
LOGO_CID_NAME: Final[str] = "logo"
PASSWORD_RESET_FORM_TEMPLATE: Final[str] = "registration/password_reset_form.html"
PASSWORD_RESET_HTML_TEMPLATE: Final[str] = "registration/password_reset_email.html"
PASSWORD_RESET_TEXT_TEMPLATE: Final[str] = "registration/password_reset_email.txt"
PASSWORD_RESET_SUBJECT_TEMPLATE: Final[str] = "registration/password_reset_subject.txt"
SUPPORT_EMAIL: Final[str] = "YOUR_SUPPORT_EMAIL_ADDRESS"
FROM_EMAIL: Final[str] = f"YOUR_COMPANY_NAME Support <{SUPPORT_EMAIL}>"
PasswordResetForm.send_email()
。def get_as_mime_image(image_file_path: str, cid_name: str) -> MIMEImage:
"""Fetch an image file and return it wrapped in a ``MIMEImage`` object for use
in emails.
After the ``MIMEImage`` has been attached to an email, reference the image in
the HTML using the Content ID.
Example:
If the CID name is "logo", then the HTML reference would be:
<img src="cid:logo" />
Args:
image_file_path: The path of the image. The path must be findable by the
Django staticfiles app.
cid_name: The Content-ID name to use within the HTML email body to
reference the image.
Raises:
FileNotFoundError: If the image file cannot be found by the staticfiles app.
Returns:
MIMEImage: The image wrapped in a ``MIMEImage`` object and the Content ID
set to ``cid_name``.
"""
paths = finders.find(image_file_path)
if paths is None:
raise FileNotFoundError(f"{image_file_path} not found in static files")
if isinstance(paths, list):
final_path = paths[0]
else:
final_path = paths
with open(final_path, 'rb') as f:
image_data = f.read()
mime_image = MIMEImage(image_data)
mime_image.add_header("Content-ID", f"<{cid_name}>")
return mime_image
class CustomPasswordResetForm(PasswordResetForm):
"""Override the default Django password-reset form to send the password reset email using both HTML and plain text.
"""
def send_mail(
self,
subject_template_name: str,
email_template_name: str,
context: Dict[str, str],
from_email: Optional[str],
to_email: str,
html_email_template_name: Optional[str] = None,
) -> None:
"""Send a ``django.core.mail.EmailMultiAlternatives`` to ``to_email``.
This method also attaches the company logo, which can be added to the
email HTML template using:
<img src="cid:logo" />
Args:
subject_template_name: Path of the template to use as the email
subject.
email_template_name: Path of the template to use for the plain text
email body.
context: A context dictionary to use when rendering the password reset
email templates.
from_email: The From email address.
to_email: The To email address.
html_email_template_name: Optional; Path of the template to use for
the HTML email body. Defaults to None.
"""
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=from_email, to=[to_email],
reply_to=[from_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.mixed_subtype = "related"
mime_image = get_as_mime_image(image_file_path=LOGO_FILE_PATH, cid_name=LOGO_CID_NAME)
email_message.attach(mime_image) # type: ignore
email_message.send()
django.contrib.auth.views.PasswordResetView.form_class
以使用新的 PasswordResetForm
子类。from django.contrib.auth import views
from your_app.forms import CustomPasswordResetForm
views.PasswordResetView.form_class = CustomPasswordResetForm
urlpatterns = [
path('', home_view, name='home'),
path('accounts/', include('django.contrib.auth.urls')),
...
]
"""Module that overrides the default Django password reset functionality by
sending emails containing both plain text as well as HTML along with the company logo.
"""
from email.mime.image import MIMEImage
from typing import Dict, Final, Optional
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.tokens import default_token_generator, PasswordResetTokenGenerator
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.staticfiles import finders
from django.core.handlers.wsgi import WSGIRequest
from django.core.mail import EmailMultiAlternatives
from django.template import loader
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
# Constants for sending password-reset emails.
LOGO_FILE_PATH: Final[str] = "img/logo.png"
LOGO_CID_NAME: Final[str] = "logo"
PASSWORD_RESET_FORM_TEMPLATE: Final[str] = "registration/password_reset_form.html"
PASSWORD_RESET_HTML_TEMPLATE: Final[str] = "registration/password_reset_email.html"
PASSWORD_RESET_TEXT_TEMPLATE: Final[str] = "registration/password_reset_email.txt"
PASSWORD_RESET_SUBJECT_TEMPLATE: Final[str] = "registration/password_reset_subject.txt"
SUPPORT_EMAIL: Final[str] = "YOUR_SUPPORT_EMAIL_ADDRESS"
FROM_EMAIL: Final[str] = f"YOUR_COMPANY_NAME Support <{SUPPORT_EMAIL}>"
def get_as_mime_image(image_file_path: str, cid_name: str) -> MIMEImage:
"""Fetch an image file and return it wrapped in a ``MIMEImage`` object for use
in emails.
After the ``MIMEImage`` has been attached to an email, reference the image in
the HTML using the Content ID.
Example:
If the CID name is "logo", then the HTML reference would be:
<img src="cid:logo" />
Args:
image_file_path: The path of the image. The path must be findable by the
Django staticfiles app.
cid_name: The Content-ID name to use within the HTML email body to
reference the image.
Raises:
FileNotFoundError: If the image file cannot be found by the staticfiles app.
Returns:
MIMEImage: The image wrapped in a ``MIMEImage`` object and the Content ID
set to ``cid_name``.
"""
paths = finders.find(image_file_path)
if paths is None:
raise FileNotFoundError(f"{image_file_path} not found in static files")
if isinstance(paths, list):
final_path = paths[0]
else:
final_path = paths
with open(final_path, 'rb') as f:
image_data = f.read()
mime_image = MIMEImage(image_data)
mime_image.add_header("Content-ID", f"<{cid_name}>")
return mime_image
class CustomPasswordResetForm(PasswordResetForm):
"""Override the default Django password-reset form to send the password reset email using both HTML and plain text.
"""
def send_mail(
self,
subject_template_name: str,
email_template_name: str,
context: Dict[str, str],
from_email: Optional[str],
to_email: str,
html_email_template_name: Optional[str] = None,
) -> None:
"""Send a ``django.core.mail.EmailMultiAlternatives`` to ``to_email``.
This method also attaches the company logo, which can be added to the
email HTML template using:
<img src="cid:logo" />
Args:
subject_template_name: Path of the template to use as the email
subject.
email_template_name: Path of the template to use for the plain text
email body.
context: A context dictionary to use when rendering the password reset
email templates.
from_email: The From email address.
to_email: The To email address.
html_email_template_name: Optional; Path of the template to use for
the HTML email body. Defaults to None.
"""
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=from_email, to=[to_email],
reply_to=[from_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.mixed_subtype = "related"
mime_image = get_as_mime_image(image_file_path=LOGO_FILE_PATH, cid_name=LOGO_CID_NAME)
email_message.attach(mime_image) # type: ignore
email_message.send()
def save(
self,
domain_override: Optional[str] = None,
subject_template_name: str = PASSWORD_RESET_SUBJECT_TEMPLATE,
email_template_name: str = PASSWORD_RESET_TEXT_TEMPLATE,
use_https: Optional[bool] = None,
token_generator: PasswordResetTokenGenerator = default_token_generator,
from_email: Optional[str] = FROM_EMAIL,
request: Optional[WSGIRequest] = None,
html_email_template_name: Optional[str] = PASSWORD_RESET_HTML_TEMPLATE,
extra_email_context: Optional[Dict[str, str]] = None
) -> None:
"""Generate a one-use only link for resetting password and email it to
the user.
Args:
domain_override: Optional; Domain name to use in the email message
template that overrides the actual domain from which the email is
sent. Defaults to None.
subject_template_name: Optional; Warning: this argument is overridden
by the global variable ``PASSWORD_RESET_SUBJECT_TEMPLATE``.
email_template_name: Optional; Warning: this argument is overridden by
the global variable ``PASSWORD_RESET_TEXT_TEMPLATE``.
use_https: Optional; If True, use HTTPS, otherwise use HTTP. Defaults
to False. Note that if the password reset HTTP request is received
via HTTPS, `use_https` will be set to True by the auth view.
token_generator: Optional; Strategy object used to generate and check
tokens for the password reset mechanism. Defaults to an instance
of ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
from_email: Optional; Warning: this argument is overridden by the
global variable``FROM_EMAIL``.
request: Optional; The HttpRequest object. Defaults to None.
html_email_template_name: Warning: this argument is overridden by the
global variable ``PASSWORD_RESET_HTML_TEMPLATE``.
extra_email_context: Optional; Key-value pairs to add to the context
dictionary used to render the password reset email templates.
Defaults to None.
"""
email_template_name = PASSWORD_RESET_TEXT_TEMPLATE
from_email = FROM_EMAIL
html_email_template_name = PASSWORD_RESET_HTML_TEMPLATE
subject_template_name = PASSWORD_RESET_SUBJECT_TEMPLATE
email = self.cleaned_data["email"]
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
UserModel = get_user_model()
email_field_name = UserModel.get_email_field_name() # type: ignore
for user in self.get_users(email):
user_email = getattr(user, email_field_name)
context = {
'email': user_email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
**(extra_email_context or {}),
}
self.send_mail(
subject_template_name = subject_template_name,
email_template_name = email_template_name,
context = context,
from_email = from_email,
to_email = user_email,
html_email_template_name = html_email_template_name
)
答案 3 :(得分:1)
您可以使用PasswordResetSerializer http://django-rest-auth.readthedocs.io/en/latest/configuration.html
然后您可以覆盖所有表单选项:
domain_override subject_template_name email_template_name use_https token_generator FROM_EMAIL 请求 html_email_template_name extra_email_context
在我的情况下,我只是覆盖2道具
class CustomPasswordResetSerializer(PasswordResetSerializer):
def get_email_options(self):
return {
'domain_override': 'anydomain.com',
'html_email_template_name': 'your_temp/password_reset_email.html',
}
答案 4 :(得分:0)
经过一些试验和错误后,我发现了在最新版本的Django(1.8)中提供自定义模板化密码重置电子邮件的简洁方法。
在project/urls.py
中,添加以下导入:
from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse_lazy
在通常的django contrib auth url路由包含之前,在你的urlpatterns中添加以下路由:
url(r'^accounts/password/reset/$',
auth_views.password_reset,
{
'post_reset_redirect': reverse_lazy('auth_password_reset_done'),
'html_email_template_name': 'registration/password_reset_html_email.html'
},
name='auth_password_reset'),
url('^', include('django.contrib.auth.urls')),
然后,在您应用的templates/registration
文件夹中,使用您想要的任何HTML模板创建password_reset_html_email.html
。
这似乎是必要的原因在于django/contrib/auth/views.py
的源代码,它具有原始URL路由映射到的视图函数:
147 def password_reset(request, is_admin_site=False,
148 template_name='registration/password_reset_form.html',
149 email_template_name='registration/password_reset_email.html',
150 subject_template_name='registration/password_reset_subject.txt',
151 password_reset_form=PasswordResetForm,
152 token_generator=default_token_generator,
153 post_reset_redirect=None,
154 from_email=None,
155 current_app=None,
156 extra_context=None,
157 html_email_template_name=None):
158
html_email_template_name
默认设置为None
,除了为此案例重写此特定路由之外,似乎没有办法分配其值,如上所述。< / p>
希望这有助于无需复制粘贴一堆几乎完全相同的代码,就像建议的其他一些答案一样 - 当然欢迎反馈!
答案 5 :(得分:0)
对我来说,这项研究耗费时间,但解决方案相当简单。 没有覆盖没有摆弄表格或类似的东西 我正在使用Django == 1.8.6,但至少应该从django 1.7开始工作。要在password_reset中启用对html格式的电子邮件的支持,我所要做的就是从email_template_name =&#39; emails / password_reset_email_html.html
更改重置功能中的电子邮件模板密钥名称到
<强> HTML 强> _email_template_name =&#39;电子邮件/ password_reset_email_html.html&#39;,
因此复位功能看起来像这样:
def reset(request):
# Wrap the built-in password reset view and pass it the arguments
# like the template name, email template name, subject template name
# and the url to redirect after the password reset is initiated.
return password_reset(request, template_name='profile/reset.html',
html_email_template_name='emails/password_reset_email_html.html',
subject_template_name='emails/reset_subject.txt',
post_reset_redirect=reverse('success'))
答案 6 :(得分:0)
根据Cem Kozinoglu解决方案,我建议更改表单并以下列方式重载send_mail而不是save方法:
class CustomPasswordResetForm(PasswordResetForm):
def send_mail(self, subject_template_name, email_template_name,
context, from_email, to_email, html_email_template_name=None):
"""
Sends 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])
# New line introduce
email_message.attach_alternative(body, 'text/html')
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()
答案 7 :(得分:0)
这是为我工作的简单方法。以对我有用的方式添加我们的自定义模板路径。
path('users/password/reset/', password_reset, {'html_email_template_name': 'registration/password_reset_email.html'},name='password_reset'),
答案 8 :(得分:0)
覆盖 PasswordResetForm ,并在email_message初始化后添加以下代码行,我解决了问题:
objectMapper.writer().withRootName(TestClass.class.getSimpleName()).writeValueAsString(list);