Ticket #17193: send_templated_mail.3.diff

File send_templated_mail.3.diff, 11.2 KB (added by tomchristie, 3 years ago)
  • django/shortcuts/__init__.py

    diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py
    index 9f97cae..4d05c23 100644
    a b of MVC. In other words, these functions/classes introduce controlled coupling 
    44for convenience's sake.
    55"""
    66
    7 from django.template import loader, RequestContext
     7from django.template import loader, loader_tags, Context, RequestContext
    88from django.http import HttpResponse, Http404
    99from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
    1010from django.db.models.manager import Manager
    1111from django.db.models.query import QuerySet
    12 from django.core import urlresolvers
     12from django.core import urlresolvers, mail
     13from django.utils.html import strip_tags
    1314
    1415def render_to_response(*args, **kwargs):
    1516    """
    def get_list_or_404(klass, *args, **kwargs): 
    128129        raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
    129130    return obj_list
    130131
     132
     133def _get_block_node(template, name):
     134    """
     135    Get a named BlockNode from a template.
     136    Returns `None` if a node with the given name does not exist.
     137    """
     138    for node in template.nodelist.get_nodes_by_type(loader_tags.BlockNode):
     139        if node.name == name:
     140            return node
     141    return None
     142
     143
     144def _render_block_node(template, name, context):
     145    """
     146    Shortcut to render a named node from a template, using the given context.
     147    Returns `None` if a node with the given name does not exist.
     148
     149    Note that leading and trailing whitespace is stripped from the output.
     150    """
     151    node = _get_block_node(template, name)
     152    if node is None:
     153        return None
     154    return node.render(context).strip()
     155
     156
     157def _create_message(subject, plain, html,
     158                    from_email, recipient_list,
     159                    cc=None, bcc=None, files=None, **kwargs):
     160    """
     161    Return an EmailMessage instance, containing either a plaintext
     162    representation, or a multipart html/plaintext representation.
     163    """
     164    if html:
     165        message = mail.EmailMultiAlternatives(
     166            subject or '',
     167            plain or '',
     168            from_email,
     169            recipient_list,
     170            cc=cc,
     171            bcc=bcc,
     172            **kwargs
     173        )
     174        message.attach_alternative(html, 'text/html')
     175
     176    else:
     177        message = mail.EmailMessage(
     178            subject or '',
     179            plain or '',
     180            from_email,
     181            recipient_list,
     182            cc=cc,
     183            bcc=bcc,
     184            **kwargs
     185        )
     186
     187    for file in files or []:
     188        message.attach_file(file)
     189
     190    return message
     191
     192
     193def _render_mail(template_name, from_email, recipient_list,
     194                 dictionary=None, context_instance=None,
     195                 cc=None, bcc=None, files=None, fail_silently=False,
     196                 html_to_plaintext=strip_tags,
     197                 **kwargs):
     198    """
     199    Returns an EmailMessage instance, rendering the subject and body of the
     200    email from a template.
     201
     202    For usage, see `send_templated_mail`.
     203
     204    Additionally, the following arguments are used:
     205
     206    `html_to_plaintext` - A function taking a single argument.  If an 'html'
     207                          block is provided, but a 'plain' block is not, then
     208                          this function will be used to auto-generate the
     209                          plaintext of the email body from the html.
     210                          May be set to `None` to disable this behaviour.
     211    `**kwargs` - Remaining keyword arguments are passed to the EmailMessage
     212                 on initialisation. (Eg. 'attachments', 'headers', etc...)
     213    """
     214    context_instance = context_instance or Context()
     215    dictionary = dictionary or {}
     216    template = loader.get_template(template_name)
     217
     218    context_instance.update(dictionary)
     219    try:
     220        subject = _render_block_node(template, 'subject', context_instance)
     221        plain = _render_block_node(template, 'plain', context_instance)
     222        html = _render_block_node(template, 'html', context_instance)
     223    finally:
     224        # Revert the context_instance to keep it in the same state.
     225        context_instance.pop()
     226
     227    # Always strip newlines from subject.
     228    if subject:
     229        subject = ' '.join(subject.splitlines())
     230
     231    # Auto-generate plaintext portion of the email if required.
     232    if plain is None and html and html_to_plaintext:
     233        plain = html_to_plaintext(html)
     234
     235    message = _create_message(subject, plain, html,
     236                              from_email, recipient_list, **kwargs)
     237    return message
     238
     239
     240def send_templated_mail(template_name, from_email, recipient_list,
     241                        dictionary=None, context_instance=None,
     242                        cc=None, bcc=None, files=None, fail_silently=False):
     243    """
     244    Sends an email, rendering the subject and body of the email from a
     245    template.
     246
     247    The template should contain a block named 'subject', and either/both of a
     248    'plain' and/or 'html' block.
     249
     250    * If only the 'plain' block exists, a plaintext email will be returned.
     251    * If only the 'html' block exists, the plaintext component will be
     252      automatically generated from the html, and a multipart email will be
     253      returned.
     254    * If both the 'plain' and 'html' blocks exist, a multipart email will be
     255      returned.
     256
     257    Required arguments:
     258
     259    `template_name` - The template that should be used to render the email.
     260    `from_email` - The sender's email address.
     261    `recipient_list` - A list of reciepient's email addresses.
     262
     263    Optional arguments:
     264
     265    `dictionary` - The context dictionary used to render the template.
     266                   By default, this is an empty dictionary.
     267    `context_instance` - The Context instance used to render the template.
     268                         By default, the template will be rendered with a
     269                         Context instance (filled with values from dictionary).
     270    `cc` - A list or tuple of recipient addresses used in the "Cc" header when
     271           sending the email.
     272    `bcc` - A list or tuple of addresses used in the "Bcc" header when sending
     273            the email.
     274    `files` - A list of file paths.  These files will be added as attachments
     275              on the message.
     276    ``fail_silently``: A boolean. If it's ``False``, ``send_templated_mail``
     277                       will raise an ``smtplib.SMTPException`` on failure.
     278    """
     279    connection = mail.get_connection()
     280    message = _render_mail(template_name, from_email, recipient_list,
     281                           dictionary, context_instance,
     282                           cc=cc, bcc=bcc, files=files,
     283                           fail_silently=fail_silently)
     284    connection.send_messages([message])
  • docs/topics/email.txt

    diff --git a/docs/topics/email.txt b/docs/topics/email.txt
    index 5979951..61cc702 100644
    a b set, are used to authenticate to the SMTP server, and the 
    3636    The character set of email sent with ``django.core.mail`` will be set to
    3737    the value of your :setting:`DEFAULT_CHARSET` setting.
    3838
     39Using templates for email
     40=========================
     41
     42Alternatively, if you want to use templates for your email subject and body
     43text you can use the :func:`~django.shortcuts.send_templated_mail` shortcut::
     44
     45    from django.shortcuts import send_templated_mail
     46
     47    send_templated_mail('emails/email.txt', 'from@example.com',
     48        ['to@example.com'], fail_silently=False)
     49
     50``emails/email.txt``::
     51
     52    {% block subject %}Subject here{% endblock %}
     53
     54    {% block plain %}
     55    Here is the message.
     56    {% endblock %}
     57
    3958send_mail()
    4059===========
    4160
    are required. 
    5473      member of ``recipient_list`` will see the other recipients in the "To:"
    5574      field of the email message.
    5675    * ``fail_silently``: A boolean. If it's ``False``, ``send_mail`` will raise
    57       an ``smtplib.SMTPException``. See the `smtplib docs`_ for a list of
    58       possible exceptions, all of which are subclasses of ``SMTPException``.
     76      an ``smtplib.SMTPException`` on failure. See the `smtplib docs`_ for a
     77      list of possible exceptions, all of which are subclasses of
     78      ``SMTPException``.
    5979    * ``auth_user``: The optional username to use to authenticate to the SMTP
    6080      server. If this isn't provided, Django will use the value of the
    6181      :setting:`EMAIL_HOST_USER` setting.
  • docs/topics/http/shortcuts.txt

    diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt
    index aa45396..7ad3093 100644
    a b will be returned:: 
    215215        object = MyModel.objects.get(...)
    216216        return redirect(object, permanent=True)
    217217
     218``send_templated_mail``
     219=======================
     220
     221.. function:: send_templated_mail(template_name, from_email, recipient_list[, dictionary=None][, context_instance=None][, cc=None][, bcc=None][, files=None][, fail_silently=False])
     222
     223   Sends a mail, rendering the subject and body of the email from a template.
     224
     225   The template should contain a block named 'subject', and either/both of a
     226   'plain' and/or 'html' block.
     227
     228   * If only the 'plain' block exists, a plaintext email will be sent.
     229
     230   * If only the 'html' block exists, the plaintext component will be
     231     automatically generated from the html, and a multipart email will be sent.
     232
     233   * If both the 'plain' and 'html' blocks exist, a multipart email will be
     234     sent.
     235
     236.. admonition:: Note
     237
     238    If you do not include a 'plain' block in your email template, be aware of
     239    how users viewing the email as plaintext will see the email.  In
     240    particular, any links contained in the body text will have been rendered as
     241    plaintext.
     242
     243Required arguments
     244------------------
     245
     246``template_name``
     247  The full name of the template to use.
     248
     249``from_email``
     250   The sender's email address.
     251
     252``recipient_list``
     253   A list of recipient's email addresses.
     254
     255Optional arguments
     256------------------
     257
     258``dictionary``
     259   The context dictionary used to render the template.
     260   By default, this is an empty dictionary.
     261
     262``context_instance``
     263    The context instance to render the template with. By default, the template
     264    will be rendered with a :class:`~django.template.Context` instance (filled
     265    with values from ``dictionary``).
     266
     267``cc``
     268    A list or tuple of recipient addresses used in the "Cc" header when sending
     269    the email.
     270
     271``bcc``
     272    A list or tuple of addresses used in the "Bcc" header when sending the
     273    email.
     274
     275``files``
     276    A list of file paths.  These files will be added as attachments on the
     277    message.
     278
     279``fail_silently``
     280    A boolean.  If it's ``False``, ``send_templated_mail`` will raise an
     281    ``smtplib.SMTPException`` on failure.
     282
     283Example
     284-------
     285
     286The following example sends a MultiPart email, with HTML content rendered
     287from a template, and plaintext content automatically generated from the HTML.
     288
     289``views.py``::
     290
     291   def my_view(request):
     292       ...
     293       recipients = [request.user.email]
     294       send_templated_email('emails/welcome.html', 'noreply@example.com', recipients)
     295
     296``welcome.html``::
     297
     298   {% block subject %}Welcome to MySite{% endblock %}
     299
     300   {% block html %}
     301   <h1>Hello, You!<h1>
     302   <p>You've been signed up.<p>
     303   {% endblock %}
     304
    218305``get_object_or_404``
    219306=====================
    220307
Back to Top