Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ venv/

# Mac OSX
.DS_Store
.idea
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ language: python
python:
- "2.7"
- "3.4"
env:
- DJANGO_VERSION=1.7
- DJANGO_VERSION=1.8
- DJANGO_VERSION=1.9b1
install:
- pip install -r dev-requirements.txt
- pip uninstall django --yes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than install and uninstall, can we just create a separate test-requirements.txt without Django in it and use that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could do that but then we've to update documentation. i thought it's much easier. at least for now, until we introduce more other similar cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how so? this is just for CI

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do this afterwards in a separate ticket - I think we can reduce the build times by doing this

- pip install -q django==$DJANGO_VERSION
- pip install coveralls
- pip install -e .
before_script:
Expand Down
10 changes: 10 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ Here at SparkPost, our messages are known as transmissions. Let's use the underl

.. _transmissions API: https://www.sparkpost.com/api#/reference/transmissions

Django Integration
------------------
The SparkPost python library comes with an email backend for Django. Put the following configuration in `settings.py` file.

.. code-block:: python

SPARKPOST_API_KEY = 'API_KEY'
EMAIL_BACKEND = 'sparkpost.django.email_backend.SparkPostEmailBackend'

Replace *API_KEY* with an actual API key that you've generated in `Get a Key`_ section.

Documentation
-------------
Expand Down
1 change: 1 addition & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ requests==2.5.1
responses==0.3.0
wheel
twine
Django>=1.8,<1.9
56 changes: 56 additions & 0 deletions docs/django/backend.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Django Email Backend
====================

The SparkPost python library comes with an email backend for Django.

Configure Django
----------------

To configure Django to use SparkPost, put the following configuration in `settings.py` file.

.. code-block:: python

SPARKPOST_API_KEY = 'API_KEY'
EMAIL_BACKEND = 'sparkpost.django.email_backend.SparkPostEmailBackend'

Replace *API_KEY* with an actual API key.


Sending an email
----------------

Django is now configured to use the SparkPost email backend. You can now send mail using Django's `send_mail` method:

.. code-block:: python

from django.core.mail import send_mail

send_mail(
subject='hello from sparkpost',
message='Hello Rock stars!'
from_email='from@yourdomain.com',
recipient_list=['to@friendsdomain.com'],
html_message='<p>Hello Rock stars!</p>',
)


Supported version
-----------------
SparkPost will support all versions of Django that are within extended support period. Refer to `Django Supported_Version`_.

Current supported versions are:
* 1.7
* 1.8
* 1.9b1


.. _Django Supported_Version: https://www.djangoproject.com/download/#supported-versions


Additional documentation
------------------------

See our `Using SparkPost with Django`_ in support article.

.. _Using SparkPost with Django: https://support.sparkpost.com/customer/en/portal/articles/2169630-using-sparkpost-with-django?b_id=7411

10 changes: 10 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ Auto-generated API reference for python-sparkpost:

api

Using in Django
---------------

Configure Django to use SparkPost email backend

.. toctree::
:maxdepth: 2

django/backend


Additional documentation
------------------------
Expand Down
Empty file added sparkpost/django/__init__.py
Empty file.
76 changes: 76 additions & 0 deletions sparkpost/django/email_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend

from sparkpost import SparkPost

from .exceptions import UnsupportedContent
from .exceptions import UnsupportedParam

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for imports let's follow this convention:

  • stdlib imports first, in alphabetical order, newline after
  • library imports (e.g. pytest, django, mock) next, in alphabetical order, newline after
  • project-specific imports (e.g. sparkpost.something) next, in alphabetical order

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya, I think that's a PEP8 recommendation too. I'll update it.


class SparkPostEmailBackend(BaseEmailBackend):
"""
SparkPost wrapper for Django email backend
"""

def __init__(self, fail_silently=False, **kwargs):
super(SparkPostEmailBackend, self)\
.__init__(fail_silently=fail_silently, **kwargs)

sp_api_key = getattr(settings, 'SPARKPOST_API_KEY', None)

self.client = SparkPost(sp_api_key)

def send_messages(self, email_messages):
"""
Send emails, returns integer representing number of successful emails
"""
success = 0
for message in email_messages:
try:
response = self._send(message)
success += response['total_accepted_recipients']
except Exception:
if not self.fail_silently:
raise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this just pass the exception through? or do you need to explicitly pass the exception that was caught?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'll re-throw the caught exception

return success

def _send(self, message):
self.check_unsupported(message)
self.check_attachments(message)

params = dict(
recipients=message.to,
text=message.body,
from_email=message.from_email,
subject=message.subject
)

if hasattr(message, 'alternatives') and len(message.alternatives) > 0:
for alternative in message.alternatives:

if alternative[1] == 'text/html':
params['html'] = alternative[0]
else:
raise UnsupportedContent(
'Content type %s is not supported' % alternative[1]
)

return self.client.transmissions.send(**params)

@staticmethod
def check_attachments(message):
if len(message.attachments):
raise UnsupportedContent(
'The SparkPost Django email backend does not '
'currently support attachment.'
)

@staticmethod
def check_unsupported(message):
unsupported_params = ['cc', 'bcc', 'reply_to']
for param in unsupported_params:
if len(getattr(message, param, [])):
raise UnsupportedParam(
'The SparkPost Django email backend does not currently '
'support %s.' % param
)
6 changes: 6 additions & 0 deletions sparkpost/django/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class UnsupportedContent(Exception):
pass


class UnsupportedParam(Exception):
pass
Loading