Skip to content

Commit fb21c0d

Browse files
committed
Mailgun: Add MAILGUN_SENDER_DOMAIN setting
Allow custom MAILGUN_SENDER_DOMAIN in Anymail settings. (Replaces need to use global esp_extra.) Improve docs to cover cases where this is needed. (esp_extra sender_domain is still supported for overriding individual messages.) Fixes #26.
1 parent 3f94e69 commit fb21c0d

File tree

5 files changed

+56
-24
lines changed

5 files changed

+56
-24
lines changed

README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ or SparkPost or any other supported ESP where you see "mailgun":
100100
)
101101
102102
ANYMAIL = {
103+
# (exact settings here depend on your ESP...)
103104
"MAILGUN_API_KEY": "<your Mailgun key>",
105+
"MAILGUN_SENDER_DOMAIN": 'mg.example.com', # your Mailgun domain, if needed
104106
}
105107
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend" # or sendgrid.SendGridBackend, or...
106108
DEFAULT_FROM_EMAIL = "[email protected]" # if you don't already have this in settings

anymail/backends/mailgun.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def __init__(self, **kwargs):
1616
"""Init options from Django settings"""
1717
esp_name = self.esp_name
1818
self.api_key = get_anymail_setting('api_key', esp_name=esp_name, kwargs=kwargs, allow_bare=True)
19+
self.sender_domain = get_anymail_setting('sender_domain', esp_name=esp_name, kwargs=kwargs,
20+
allow_bare=True, default=None)
1921
api_url = get_anymail_setting('api_url', esp_name=esp_name, kwargs=kwargs,
2022
default="https://api.mailgun.net/v3")
2123
if not api_url.endswith("/"):
@@ -54,7 +56,7 @@ class MailgunPayload(RequestsPayload):
5456

5557
def __init__(self, message, defaults, backend, *args, **kwargs):
5658
auth = ("api", backend.api_key)
57-
self.sender_domain = None
59+
self.sender_domain = backend.sender_domain
5860
self.all_recipients = [] # used for backend.parse_recipient_status
5961

6062
# late-binding of recipient-variables:

docs/esps/mailgun.rst

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ root of the settings file if neither ``ANYMAIL["MAILGUN_API_KEY"]``
4141
nor ``ANYMAIL_MAILGUN_API_KEY`` is set.
4242

4343

44+
.. setting:: ANYMAIL_MAILGUN_SENDER_DOMAIN
45+
46+
.. rubric:: MAILGUN_SENDER_DOMAIN
47+
48+
If you are using a specific `Mailgun sender domain`_
49+
that is *different* from your messages' `from_email` domains,
50+
set this to the domain you've configured in your Mailgun account.
51+
52+
If your messages' `from_email` domains always match a configured
53+
Mailgun sender domain, this setting is not needed.
54+
55+
See :ref:`mailgun-sender-domain` below for examples.
56+
57+
4458
.. setting:: ANYMAIL_MAILGUN_API_URL
4559

4660
.. rubric:: MAILGUN_API_URL
@@ -58,32 +72,40 @@ The default is ``MAILGUN_API_URL = "https://api.mailgun.net/v3"``
5872
Email sender domain
5973
-------------------
6074

61-
Mailgun's API requires a sender domain `in the API url <base-url>`_.
62-
By default, Anymail will use the domain of each email's from address
63-
as the domain for the Mailgun API.
75+
Mailgun's API requires identifying the sender domain.
76+
By default, Anymail uses the domain of each messages's `from_email`
77+
(e.g., "example.com" for "from\@example.com").
78+
79+
You will need to override this default if you are using
80+
a dedicated `Mailgun sender domain`_ that is different from
81+
a message's `from_email` domain.
6482

65-
If you need to override this default, you can use Anymail's
66-
:attr:`esp_extra` dict, either on an individual message:
83+
For example, if you are sending from "orders\@example.com", but your
84+
Mailgun account is configured for "*mail1*.example.com", you should provide
85+
:setting:`MAILGUN_SENDER_DOMAIN <ANYMAIL_MAILGUN_SENDER_DOMAIN>` in your settings.py:
6786

6887
.. code-block:: python
88+
:emphasize-lines: 4
6989
70-
message = EmailMessage(from_email="[email protected]", ...)
71-
message.esp_extra = {"sender_domain": "example.com"}
90+
ANYMAIL = {
91+
...
92+
"MAILGUN_API_KEY": "<your API key>",
93+
"MAILGUN_SENDER_DOMAIN": "mail1.example.com"
94+
}
7295
7396
74-
... or as a global :ref:`send default <send-defaults>` setting that applies
75-
to all messages:
97+
If you need to override the sender domain for an individual message,
98+
include `sender_domain` in Anymail's :attr:`~anymail.message.AnymailMessage.esp_extra`
99+
for that message:
76100

77101
.. code-block:: python
78102
79-
ANYMAIL = {
80-
...
81-
"MAILGUN_SEND_DEFAULTS": {
82-
"esp_extra": {"sender_domain": "example.com"}
83-
}
84-
}
103+
message = EmailMessage(from_email="[email protected]", ...)
104+
message.esp_extra = {"sender_domain": "mail2.example.com"}
105+
85106
86-
.. _base-url: https://documentation.mailgun.com/api-intro.html#base-url
107+
.. _Mailgun sender domain:
108+
https://help.mailgun.com/hc/en-us/articles/202256730-How-do-I-pick-a-domain-name-for-my-Mailgun-account-
87109

88110

89111
.. _mailgun-esp-extra:

tests/test_mailgun_backend.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,8 @@ def test_only_merge_global_data(self):
383383

384384
def test_sender_domain(self):
385385
"""Mailgun send domain can come from from_email or esp_extra"""
386-
# You could also use ANYMAIL_SEND_DEFAULTS={'esp_extra': {'sender_domain': 'your-domain.com'}}
387-
# (The mailgun_integration_tests do that.)
386+
# You could also use MAILGUN_SENDER_DOMAIN in your ANYMAIL settings, as in the next test.
387+
# (The mailgun_integration_tests also do that.)
388388
self.message.from_email = "Test From <[email protected]>"
389389
self.message.send()
390390
self.assert_esp_called('/from-email.example.com/messages') # API url includes the sender-domain
@@ -393,6 +393,11 @@ def test_sender_domain(self):
393393
self.message.send()
394394
self.assert_esp_called('/esp-extra.example.com/messages') # overrides from_email
395395

396+
@override_settings(ANYMAIL_MAILGUN_SENDER_DOMAIN='mg.example.com')
397+
def test_sender_domain_setting(self):
398+
self.message.send()
399+
self.assert_esp_called('/mg.example.com/messages') # setting overrides from_email
400+
396401
def test_default_omits_options(self):
397402
"""Make sure by default we don't send any ESP-specific options.
398403

tests/test_mailgun_integration.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323
@unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN,
2424
"Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables "
2525
"to run Mailgun integration tests")
26-
@override_settings(ANYMAIL_MAILGUN_API_KEY=MAILGUN_TEST_API_KEY,
27-
ANYMAIL_MAILGUN_SEND_DEFAULTS={
28-
'esp_extra': {'o:testmode': 'yes',
29-
'sender_domain': MAILGUN_TEST_DOMAIN}},
26+
@override_settings(ANYMAIL={'MAILGUN_API_KEY': MAILGUN_TEST_API_KEY,
27+
'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
28+
'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}},
3029
EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend")
3130
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
3231
"""Mailgun API integration tests
@@ -168,7 +167,9 @@ def test_invalid_from(self):
168167
self.assertEqual(err.status_code, 400)
169168
self.assertIn("'from' parameter is not a valid address", str(err))
170169

171-
@override_settings(ANYMAIL_MAILGUN_API_KEY="Hey, that's not an API key!")
170+
@override_settings(ANYMAIL={'MAILGUN_API_KEY': "Hey, that's not an API key",
171+
'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
172+
'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}})
172173
def test_invalid_api_key(self):
173174
with self.assertRaises(AnymailAPIError) as cm:
174175
self.message.send()

0 commit comments

Comments
 (0)