Code of Conduct
Feature Description
Break up PasswordResetForm.send_mail() to allow custom subclasses to override email rendering and email sending independently, without having to duplicate all its existing logic.
Problem
With the introduction of MAILERS in Django 6.1, projects may wish to use a non-"default" mailer config for sending password reset emails. (E.g., to use a special high-priority queue.)
The current PasswordResetForm.send_mail() implementation combines rendering and composing the email with sending the resulting message. A subclass that overrides it to change to email_message.send(using="priority") would need to also duplicate all of the rendering code—and then carefully track any future changes in the base class.
DEP 0018 suggested (as MAILERS follow-on work) adding a PasswordResetForm.email_using property to simplify this. But that wouldn't handle cases where the mailer choice needs to be dynamic based on other factors, like the user's region: email_message.send(using="eu" if context["user"].gdpr_applies() else "default").
Similarly, there may be cases where a project wants to adjust password reset email rendering without duplicating the sending code. (Adding to the context, ensuring the email uses the user's locale rather than the request's, etc.)
Request or proposal
proposal
Additional Details
No response
Implementation Suggestions
Suggestion:
class PasswordResetForm(forms.Form):
...
email_using = None
def send_mail(
self,
subject_template_name,
email_template_name,
context,
from_email,
to_email,
html_email_template_name=None,
):
email_message = self.build_email_message(
subject_template_name=subject_template_name,
email_template_name=email_template_name,
html_email_template_name=html_email_template_name,
context=context,
from_email=from_email,
to_email=to_email,
)
try:
self.send_email_message(
email_message=email_message, context=context
)
except Exception:
logger.exception(
"Failed to send password reset email to %s", context["user"].pk
)
def build_email_message(
self,
*,
subject_template_name,
email_template_name,
html_email_template_name,
context,
from_email,
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")
return email_message
def send_email_message(self, *, email_message, context):
email_message.send(using=self.email_using)
...
The email_using property is there to simplify non-dynamic cases, but isn't strictly required. (It's called email_using to avoid any potential confusion with databases' using.)
context is passed to send_email_message() to give access to the user, domain (site), etc. (The from_email and to_email are available in attributes of email_message, so aren't repeated as separate args.)
Code of Conduct
Feature Description
Break up
PasswordResetForm.send_mail()to allow custom subclasses to override email rendering and email sending independently, without having to duplicate all its existing logic.Problem
With the introduction of
MAILERSin Django 6.1, projects may wish to use a non-"default"mailer config for sending password reset emails. (E.g., to use a special high-priority queue.)The current
PasswordResetForm.send_mail()implementation combines rendering and composing the email with sending the resulting message. A subclass that overrides it to change toemail_message.send(using="priority")would need to also duplicate all of the rendering code—and then carefully track any future changes in the base class.DEP 0018 suggested (as
MAILERSfollow-on work) adding aPasswordResetForm.email_usingproperty to simplify this. But that wouldn't handle cases where the mailer choice needs to be dynamic based on other factors, like the user's region:email_message.send(using="eu" if context["user"].gdpr_applies() else "default").Similarly, there may be cases where a project wants to adjust password reset email rendering without duplicating the sending code. (Adding to the context, ensuring the email uses the user's locale rather than the request's, etc.)
Request or proposal
proposal
Additional Details
No response
Implementation Suggestions
Suggestion:
The
email_usingproperty is there to simplify non-dynamic cases, but isn't strictly required. (It's calledemail_usingto avoid any potential confusion with databases'using.)contextis passed tosend_email_message()to give access to the user, domain (site), etc. (Thefrom_emailandto_emailare available in attributes ofemail_message, so aren't repeated as separate args.)