diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index f33313d93..7c772372b 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -57,6 +57,7 @@ Listed in alphabetical order. Andreas Meistad `@ameistad`_ Andres Gonzalez `@andresgz`_ Andrew Mikhnevich `@zcho`_ + Andrew Chen Wang `@Andrew-Chen-Wang`_ Andy Rose Anna Callahan `@jazztpt`_ Anna Sidwell `@takkaria`_ @@ -234,6 +235,7 @@ Listed in alphabetical order. .. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza .. _@andresgz: https://github.com/andresgz .. _@antoniablair: https://github.com/antoniablair +.. _@Andrew-Chen-Wang: https://github.com/Andrew-Chen-Wang .. _@apirobot: https://github.com/apirobot .. _@archinal: https://github.com/archinal .. _@areski: https://github.com/areski diff --git a/README.rst b/README.rst index 3add09cf3..56db4c785 100644 --- a/README.rst +++ b/README.rst @@ -46,7 +46,7 @@ Features * Registration via django-allauth_ * Comes with custom user model ready to go * Optional custom static build using Gulp and livereload -* Send emails via Anymail_ (using Mailgun_ by default, but switchable) +* Send emails via Anymail_ (using Mailgun_ by default or Amazon SES if AWS is selected cloud provider, but switchable) * Media storage using Amazon S3 or Google Cloud Storage * Docker support using docker-compose_ for development and production (using Traefik_ with LetsEncrypt_ support) * Procfile_ for deploying to Heroku @@ -85,7 +85,7 @@ Optional Integrations .. _PythonAnywhere: https://www.pythonanywhere.com/ .. _Traefik: https://traefik.io/ .. _LetsEncrypt: https://letsencrypt.org/ -.. _pre-commit: https://github.com/pre-commit/pre-commit +.. _pre-commit: https://github.com/pre-commit/pre-commit Constraints ----------- diff --git a/cookiecutter.json b/cookiecutter.json index 4e77d1109..cfa1b346a 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -33,6 +33,17 @@ "GCP", "None" ], + "mail_service": [ + "Mailgun", + "Amazon SES", + "Mailjet", + "Mandrill", + "Postmark", + "Sendgrid", + "SendinBlue", + "SparkPost", + "Other SMTP" + ], "use_drf": "n", "custom_bootstrap_compilation": "n", "use_compressor": "n", diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index 2ed77facd..87ae90b1d 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -70,6 +70,19 @@ cloud_provider: Note that if you choose no cloud provider, media files won't work. +mail_service: + Select an email service that Django-Anymail provides + + 1. Amazon SES_ + 2. Mailgun_ + 3. Mailjet_ + 4. Mandrill_ + 5. Postmark_ + 6. SendGrid_ + 7. SendinBlue_ + 8. SparkPost_ + 9. Plain/Vanilla Django-Anymail_ + use_drf: Indicates whether the project should be configured to use `Django Rest Framework`_. @@ -132,6 +145,16 @@ debug: .. _AWS: https://aws.amazon.com/s3/ .. _GCP: https://cloud.google.com/storage/ +.. _SES: https://aws.amazon.com/ses/ +.. _Mailgun: https://www.mailgun.com +.. _Mailjet: https://www.mailjet.com +.. _Mandrill: http://mandrill.com +.. _Postmark: https://postmarkapp.com +.. _SendGrid: https://sendgrid.com +.. _SendinBlue: https://www.sendinblue.com +.. _SparkPost: https://www.sparkpost.com +.. _Django-Anymail: https://anymail.readthedocs.io/en/stable/ + .. _Django Rest Framework: https://github.com/encode/django-rest-framework/ .. _Django Compressor: https://github.com/django-compressor/django-compressor diff --git a/docs/settings.rst b/docs/settings.rst index e586c9634..2ae8814e9 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -52,6 +52,21 @@ DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a MAILGUN_API_KEY MAILGUN_API_KEY n/a raises error MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error MAILGUN_API_URL n/a n/a "https://api.mailgun.net/v3" +MAILJET_API_KEY MAILJET_API_KEY n/a raises error +MAILJET_SECRET_KEY MAILJET_SECRET_KEY n/a raises error +MAILJET_API_URL n/a n/a "https://api.mailjet.com/v3" +MANDRILL_API_KEY MANDRILL_API_KEY n/a raises error +MANDRILL_API_URL n/a n/a "https://mandrillapp.com/api/1.0" +POSTMARK_SERVER_TOKEN POSTMARK_SERVER_TOKEN n/a raises error +POSTMARK_API_URL n/a n/a "https://api.postmarkapp.com/" +SENDGRID_API_KEY SENDGRID_API_KEY n/a raises error +SENDGRID_GENERATE_MESSAGE_ID True n/a raises error +SENDGRID_MERGE_FIELD_FORMAT None n/a raises error +SENDGRID_API_URL n/a n/a "https://api.sendgrid.com/v3/" +SENDINBLUE_API_KEY SENDINBLUE_API_KEY n/a raises error +SENDINBLUE_API_URL n/a n/a "https://api.sendinblue.com/v3/" +SPARKPOST_API_KEY SPARKPOST_API_KEY n/a raises error +SPARKPOST_API_URL n/a n/a "https://api.sparkpost.com/api/v1" ======================================= =========================== ============================================== ====================================================================== -------------------------- diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index e51e4b9fa..91f44ae5a 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -48,6 +48,15 @@ SUPPORTED_COMBINATIONS = [ {"cloud_provider": "GCP", "use_whitenoise": "n"}, {"cloud_provider": "None", "use_whitenoise": "y"}, # Note: cloud_provider=None AND use_whitenoise=n is not supported + {"mail_service": "Mailgun"}, + {"mail_service": "Amazon SES"}, + {"mail_service": "Mailjet"}, + {"mail_service": "Mandrill"}, + {"mail_service": "Postmark"}, + {"mail_service": "Sendgrid"}, + {"mail_service": "SendinBlue"}, + {"mail_service": "SparkPost"}, + {"mail_service": "Other SMTP"}, {"use_drf": "y"}, {"use_drf": "n"}, {"js_task_runner": "None"}, @@ -75,7 +84,9 @@ SUPPORTED_COMBINATIONS = [ {"debug": "n"}, ] -UNSUPPORTED_COMBINATIONS = [{"cloud_provider": "None", "use_whitenoise": "n"}] +UNSUPPORTED_COMBINATIONS = [ + {"cloud_provider": "None", "use_whitenoise": "n"}, +] def _fixture_id(ctx): diff --git a/{{cookiecutter.project_slug}}/.envs/.production/.django b/{{cookiecutter.project_slug}}/.envs/.production/.django index ae13ee01d..e7e8461c9 100644 --- a/{{cookiecutter.project_slug}}/.envs/.production/.django +++ b/{{cookiecutter.project_slug}}/.envs/.production/.django @@ -13,9 +13,26 @@ DJANGO_SECURE_SSL_REDIRECT=False # Email # ------------------------------------------------------------------------------ -MAILGUN_API_KEY= DJANGO_SERVER_EMAIL= +{% if cookiecutter.mail_service == 'Mailgun' %} +MAILGUN_API_KEY= MAILGUN_DOMAIN= +{% elif cookiecutter.mail_service == 'Mailjet' %} +MAILJET_API_KEY= +MAILJET_SECRET_KEY= +{% elif cookiecutter.mail_service == 'Mandrill' %} +MANDRILL_API_KEY= +{% elif cookiecutter.mail_service == 'Postmark' %} +POSTMARK_SERVER_TOKEN= +{% elif cookiecutter.mail_service == 'Sendgrid' %} +SENDGRID_API_KEY= +SENDGRID_GENERATE_MESSAGE_ID=True +SENDGRID_MERGE_FIELD_FORMAT=None +{% elif cookiecutter.mail_service == 'SendinBlue' %} +SENDINBLUE_API_KEY= +{% elif cookiecutter.mail_service == 'SparkPost' %} +SPARKPOST_API_KEY= +{% endif %} {% if cookiecutter.cloud_provider == 'AWS' %} # AWS # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index a027eca17..22832d341 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -183,17 +183,80 @@ EMAIL_SUBJECT_PREFIX = env( # Django Admin URL regex. ADMIN_URL = env("DJANGO_ADMIN_URL") -# Anymail (Mailgun) +# Anymail # ------------------------------------------------------------------------------ # https://anymail.readthedocs.io/en/stable/installation/#installing-anymail INSTALLED_APPS += ["anymail"] # noqa F405 -EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend # https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference +{%- if cookiecutter.mail_service == 'Mailgun' %} +# https://anymail.readthedocs.io/en/stable/esps/mailgun/ +EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" ANYMAIL = { "MAILGUN_API_KEY": env("MAILGUN_API_KEY"), "MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"), "MAILGUN_API_URL": env("MAILGUN_API_URL", default="https://api.mailgun.net/v3"), } +{%- elif cookiecutter.mail_service == 'Amazon SES' %} +# https://anymail.readthedocs.io/en/stable/esps/amazon_ses/ +EMAIL_BACKEND = "anymail.backends.amazon_ses.EmailBackend" +ANYMAIL = {} +{%- elif cookiecutter.mail_service == 'Mailjet' %} +# https://anymail.readthedocs.io/en/stable/esps/mailjet/ +EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend" +ANYMAIL = { + "MAILJET_API_KEY": env("MAILJET_API_KEY"), + "MAILJET_SECRET_KEY": env("MAILJET_SECRET_KEY"), + "MAILJET_API_URL": env("MAILJET_API_URL", default="https://api.mailjet.com/v3"), +} +{%- elif cookiecutter.mail_service == 'Mandrill' %} +# https://anymail.readthedocs.io/en/stable/esps/mandrill/ +EMAIL_BACKEND = "anymail.backends.mandrill.EmailBackend" +ANYMAIL = { + "MANDRILL_API_KEY": env("MANDRILL_API_KEY"), + "MANDRILL_API_URL": env( + "MANDRILL_API_URL", default="https://mandrillapp.com/api/1.0" + ), +} +{%- elif cookiecutter.mail_service == 'Postmark' %} +# https://anymail.readthedocs.io/en/stable/esps/postmark/ +EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend" +ANYMAIL = { + "POSTMARK_SERVER_TOKEN": env("POSTMARK_SERVER_TOKEN"), + "POSTMARK_API_URL": env("POSTMARK_API_URL", default="https://api.postmarkapp.com/"), +} +{%- elif cookiecutter.mail_service == 'Sendgrid' %} +# https://anymail.readthedocs.io/en/stable/esps/sendgrid/ +EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend" +ANYMAIL = { + "SENDGRID_API_KEY": env("SENDGRID_API_KEY"), + "SENDGRID_GENERATE_MESSAGE_ID": env("SENDGRID_GENERATE_MESSAGE_ID"), + "SENDGRID_MERGE_FIELD_FORMAT": env("SENDGRID_MERGE_FIELD_FORMAT"), + "SENDGRID_API_URL": env("SENDGRID_API_URL", default="https://api.sendgrid.com/v3/"), +} +{%- elif cookiecutter.mail_service == 'SendinBlue' %} +# https://anymail.readthedocs.io/en/stable/esps/sendinblue/ +EMAIL_BACKEND = "anymail.backends.sendinblue.EmailBackend" +ANYMAIL = { + "SENDINBLUE_API_KEY": env("SENDINBLUE_API_KEY"), + "SENDINBLUE_API_URL": env( + "SENDINBLUE_API_URL", default="https://api.sendinblue.com/v3/" + ), +} +{%- elif cookiecutter.mail_service == 'SparkPost' %} +# https://anymail.readthedocs.io/en/stable/esps/sparkpost/ +EMAIL_BACKEND = "anymail.backends.sparkpost.EmailBackend" +ANYMAIL = { + "SPARKPOST_API_KEY": env("SPARKPOST_API_KEY"), + "SPARKPOST_API_URL": env( + "SPARKPOST_API_URL", default="https://api.sparkpost.com/api/v1" + ), +} +{%- elif cookiecutter.mail_service == 'Other SMTP' %} +# https://anymail.readthedocs.io/en/stable/esps +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +ANYMAIL = {} +{%- endif %} {% if cookiecutter.use_compressor == 'y' -%} # django-compressor diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index f0c4fed12..da9cbc97a 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -18,4 +18,22 @@ django-storages[boto3]==1.9.1 # https://github.com/jschneier/django-storages {%- elif cookiecutter.cloud_provider == 'GCP' %} django-storages[google]==1.9.1 # https://github.com/jschneier/django-storages {%- endif %} +{%- if cookiecutter.mail_service == 'Mailgun' %} django-anymail[mailgun]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Amazon SES' %} +django-anymail[amazon_ses]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Mailjet' %} +django-anymail[mailjet]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Mandrill' %} +django-anymail[mandrill]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Postmark' %} +django-anymail[postmark]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Sendgrid' %} +django-anymail[sendgrid]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'SendinBlue' %} +django-anymail[sendinblue]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'SparkPost' %} +django-anymail[sparkpost]==7.0.0 # https://github.com/anymail/django-anymail +{%- elif cookiecutter.mail_service == 'Other SMTP' %} +django-anymail==7.0.0 # https://github.com/anymail/django-anymail +{%- endif %}