Skip to content

Commit

Permalink
Add Template MultiAttachments EmailView (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
csalom authored and sastred committed Dec 20, 2019
1 parent 3b6cf49 commit ef62f55
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 5 deletions.
88 changes: 88 additions & 0 deletions django_yubin/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,94 @@ def render_to_message(self, extra_context=None, filename=None, attachment=None,
return message


class TemplatedMultipleAttachmentsEmailMessageView(TemplatedHTMLEmailMessageView):
"""
This is a sintactic sugar class to allow us to send mail messages with
multiple attachments
"""

def send(self, extra_context=None, attachments=None, **kwargs):
"""
Renders and sends an email message.
All keyword arguments other than ``extra_context`` are passed through
as keyword arguments when constructing a new :attr:`message_class`
instance for this message.
This method exists primarily for convenience, and the proper
rendering of your message should not depend on the behavior of this
method. To alter how a message is created, override
:meth:``render_to_message`` instead, since that should always be
called, even if a message is not sent.
:param extra_context: Any additional context data that will be used
when rendering this message
:param attachments: List of dicts with filename and attachment file, with keys 'filename', 'attachment'
and 'mimetype'.
Attachments example -> [{"filename": "filename.pdf", "attachment": "file-path",},...]
:param kwargs : mail settings
from_email=None,
to=None,
bcc=None,
connection=None,
cc=None
headers=None,
:type extra_context: :class:`dict`
:type attachments: :class:`list`
"""

if isinstance(attachments, list):
for attachment in attachments:
if not attachment.get("filename") or not attachment.get("attachment"):
raise Exception("'filename', 'attachment' are mandatory for every attachment")

message = self.render_to_message(extra_context=extra_context, attachments=attachments, **kwargs)
return message.send()

def render_to_message(self, extra_context=None, attachments=None, *args, **kwargs):
"""
Renders and returns an unsent message with the given context.
Any extra keyword arguments passed will be passed through as keyword
arguments to the message constructor.
:param extra_context: Any additional context to use when rendering
templated content.
:type extra_context: :class:`dict`
:param attachments: List of dicts with filename and attachment file
:type attachments: :class:`list`
:param kwargs : mail settings
from_email=None,
to=None,
bcc=None,
connection=None,
cc=None
headers=None,
:returns: A message instance.
:rtype: :attr:`.message_class`
"""

message = super(TemplatedMultipleAttachmentsEmailMessageView, self).render_to_message(
extra_context, *args, **kwargs)

if extra_context is None:
extra_context = {}
context = self.get_context_data(**extra_context)
content = self.render_html_body(context)
message.attach_alternative(content, mimetype='text/html')
if isinstance(attachments, list):
for attachment in attachments:
message.attach(filename=attachment.get("filename"), content=attachment.get("attachment"))

return message


class TemplateContextMixin(object):
subject_template = template_from_string('{% autoescape off %}{{ subject }}{% endautoescape %}')
body_template = template_from_string('{% autoescape off %}{{ content }}{% endautoescape %}')
Expand Down
49 changes: 48 additions & 1 deletion docs/mailviews.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Attachments
To add an attachment to your mail you just have to remember that `render_to_message` returns a `EmailMessage` instance,
so you can use https://docs.djangoproject.com/en/dev/topics/email/

As usually we sent just an attachment, we have created a class thats tries to save your time allowing to sent an
As usually we sent just an attachment, we have created a class that tries to save your time allowing to sent an
attachent just passing the file name or a file object

.. code:: python
Expand Down Expand Up @@ -190,6 +190,53 @@ So if we want to send in our newsletter a pdf file we could do
As an attachment you must provide the full file path or the data stream.


Multiple Attachments
--------------------

To add multiple attachments to your mail you just have to remember that `render_to_message` returns a `EmailMessage`
instance, so you can use https://docs.djangoproject.com/en/dev/topics/email/

To send multiple attachments, we have created a class that tries to save yor time allowing to set the attachments
just passing a list of dicts with the filename and the file content

.. code:: python
TemplatedMultipleAttachmentsEmailMessageView
So if we want to send in our newsletter a pdf file we could do


.. code:: python
class NewsletterView(TemplatedMultipleAttachmentsEmailMessageView):
subject_template_name = 'emails/newsletter/subject.txt'
body_template_name = 'emails/newsletter/body.txt'
html_body_template_name = 'emails/newsletter/body_html.html'
def render_to_message(self, extra_context=None, **kwargs):
kwargs['to'] = ('[email protected]',)
kwargs['from_email'] = '[email protected]'
return super(NewsletterView, self).render_to_message(extra_context=None, **kwargs)
def get_context_data(self, **kwargs):
"""
here we can get the addtional data we want
"""
context = super(NewsletterView, self).get_context_data(**kwargs)
context['day'] = datetime.date.today()
return context
# Instantiate and send a message.
attachments = [
{"attachment": os.path.join(OUR_ROOT_FILES_PATH, 'newsletter/attachment.pdf'), "filename": "attachment.pdf"},
{"attachment": os.path.join(OUR_ROOT_FILES_PATH, 'newsletter/attachment2.pdf'), "filename": "attachment2.pdf"},
{"attachment": os.path.join(OUR_ROOT_FILES_PATH, 'newsletter/attachment3.pdf'), "filename": "attachment3.pdf"},
]
NewsletterView().send(attachments=attachments)
As an attachment you must provide the full file path or the data stream.

Sending mail to a user
----------------------

Expand Down
47 changes: 43 additions & 4 deletions tests/tests/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
from django_yubin.messages import (
TemplatedEmailMessageView, TemplatedHTMLEmailMessageView,
TemplatedAttachmentEmailMessageView, template_from_string,
)

TemplatedMultipleAttachmentsEmailMessageView)

using_test_templates = override_settings(
TEMPLATE_DIRS=(
Expand Down Expand Up @@ -177,7 +176,7 @@ def add_templates_to_message(self):
"""
Adds templates to the fixture message, ensuring it can be rendered.
"""
super(TemplatedHTMLEmailMessageViewTestCase, self)\
super(TemplatedHTMLEmailMessageViewTestCase, self) \
.add_templates_to_message()
self.message.html_body_template = self.html_body_template

Expand Down Expand Up @@ -233,7 +232,7 @@ def add_templates_to_message(self):
"""
Adds templates to the fixture message, ensuring it can be rendered.
"""
super(TemplatedAttachmentEmailMessageViewTestCase, self)\
super(TemplatedAttachmentEmailMessageViewTestCase, self) \
.add_templates_to_message()
self.message.html_body_template = self.html_body_template

Expand Down Expand Up @@ -261,6 +260,46 @@ def test_send_message(self):
self.assertOutboxLengthEquals(1)


class TemplatedMultipleAttachmentsEmailMessageViewTestCase(TemplatedAttachmentEmailMessageViewTestCase):
message_class = TemplatedMultipleAttachmentsEmailMessageView

def test_send_message(self):
"""Test we can send an attachment using the send command"""
self.add_templates_to_message()
attachment = os.path.join(os.path.dirname(__file__), 'files/attachment.pdf')
attachments = [{
"filename": "attachment.pdf",
"attachment": attachment
}]
self.message.send(self.context,
attachments=attachments,
to=('[email protected]',))
self.assertOutboxLengthEquals(1)

def render_to_message(self, attach_number):
self.add_templates_to_message()
attachment = os.path.join(os.path.dirname(__file__), 'files/attachment.pdf'),
attachments = [{
"filename": "{}-attachment.pdf".format(number),
"attachment": attachment
} for number in range(attach_number)]
message = self.message.render_to_message(extra_context=self.context,
attachments=attachments)
self.assertEqual(len(message.attachments), attach_number)

def test_render_to_message(self):
"""Test we can send an attachment using the send command"""
self.render_to_message(1)

def test_render_to_message_no_attach(self):
"""Test we can send no attchaments using the send command"""
self.render_to_message(0)

def test_render_to_message_multiple_attachs(self):
"""Test we can send multiple attchaments using the send command"""
self.render_to_message(10)


class TestEmailOptions(EmailMessageViewTestCase):
message_class = TemplatedEmailMessageView

Expand Down

0 comments on commit ef62f55

Please sign in to comment.