Configuration

django-forwardemail stores its settings in a Django model so that each site in a multi-site project can have its own ForwardEmail configuration.

EmailConfiguration Model

The package uses the EmailConfiguration model to store site-specific email settings. Each Django Site has exactly one configuration.

Model Fields

class EmailConfiguration(models.Model):
    api_key = models.CharField(max_length=255)
    from_email = models.EmailField()
    from_name = models.CharField(max_length=255)
    reply_to = models.EmailField()
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = [["site"]]  # One configuration per site

All fields except the timestamps are required.

Creating Configurations

You can create email configurations through the Django admin interface or programmatically:

from django.contrib.sites.models import Site
from django_forwardemail.models import EmailConfiguration

# Get or create a site
site, created = Site.objects.get_or_create(
    domain='example.com',
    defaults={'name': 'Example Site'}
)

# Create email configuration
config = EmailConfiguration.objects.create(
    site=site,
    api_key='your_forwardemail_api_key',
    from_email='noreply@example.com',
    from_name='Example Site',
    reply_to='support@example.com',
)

Django Admin Integration

The package registers EmailConfiguration with the Django admin so you can create, view, and edit configurations for each site without touching code. The admin list view shows the site, from name, from email, and reply-to address, and supports searching and filtering by site.

Package Settings

The package itself has only one optional setting. All credentials live in the EmailConfiguration model rather than in settings or environment variables.

FORWARD_EMAIL_BASE_URL

Base URL for the ForwardEmail API. Defaults to https://api.forwardemail.net. You typically only change this for testing or a custom endpoint.

# settings.py
FORWARD_EMAIL_BASE_URL = 'https://api.forwardemail.net'

EMAIL_BACKEND

Set this to use ForwardEmail with Django’s standard email functions:

# settings.py
EMAIL_BACKEND = 'django_forwardemail.backends.ForwardEmailBackend'

Site Detection Logic

ForwardEmailService.send_email() resolves which configuration to use as follows:

  1. Explicit Site Parameter: If a site argument is passed, it is used.

  2. Request-Based Detection: If a request argument is provided, get_current_site(request) is used (it must resolve to a real Site).

  3. Current Site Fallback: Site.objects.get_current().

  4. First Site Fallback: Site.objects.first().

  5. If no site can be determined, ImproperlyConfigured is raised.

from django.contrib.sites.models import Site
from django_forwardemail.services import ForwardEmailService

# Explicit site specification
site = Site.objects.get(domain='example.com')
ForwardEmailService.send_email(
    to='user@example.com',
    subject='Welcome!',
    text='Welcome message',
    site=site,  # Explicit site
)

# Request-based detection (in views)
def my_view(request):
    ForwardEmailService.send_email(
        to='user@example.com',
        subject='Welcome!',
        text='Welcome message',
        request=request,  # Site extracted from request
    )

Multi-Site Configuration

For multi-site Django deployments, create separate configurations for each site:

from django.contrib.sites.models import Site
from django_forwardemail.models import EmailConfiguration

# Site 1: Main website
main_site = Site.objects.create(domain='example.com', name='Main Site')
EmailConfiguration.objects.create(
    site=main_site,
    api_key='main_site_api_key',
    from_email='noreply@example.com',
    from_name='Example.com',
    reply_to='support@example.com',
)

# Site 2: Blog subdomain
blog_site = Site.objects.create(domain='blog.example.com', name='Blog')
EmailConfiguration.objects.create(
    site=blog_site,
    api_key='blog_api_key',
    from_email='blog@example.com',
    from_name='Example Blog',
    reply_to='support@example.com',
)

How Defaults Are Applied

When send_email() resolves a site, it looks up that site’s EmailConfiguration and applies these rules:

  1. API key: Always taken from the configuration. If the resolved site has no EmailConfiguration, ImproperlyConfigured is raised.

  2. From email: If from_email is not passed, the configured from_name and from_email are combined into the sender. If from_email is passed without a display name, the configured from_name is added.

  3. Reply-to: If reply_to is not passed, the configured reply_to is used.

Security Considerations

API Key Storage

API keys are stored in the EmailConfiguration model. Restrict Django admin access to trusted staff, and use Django’s standard secret-management practices for your database credentials.

Key Rotation

Regularly rotate your ForwardEmail API keys:

  1. Generate a new API key in the ForwardEmail dashboard

  2. Update the EmailConfiguration (via Django admin or the ORM)

  3. Test email sending with the new key

  4. Revoke the old API key

Per-Environment Configuration

Use different API keys for development, staging, and production by storing the appropriate key in each environment’s database, or by pointing FORWARD_EMAIL_BASE_URL at a non-production endpoint during testing.

Logging Configuration

Configure logging to monitor email sending. The package logs under the django_forwardemail logger name:

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': 'django_forwardemail.log',
            'formatter': 'verbose',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django_forwardemail': {
            'handlers': ['file', 'console'],
            'level': 'INFO',
            'propagate': True,
        },
    },
}

Testing Configuration

For tests, mock the ForwardEmail API rather than sending real requests. The service issues a single requests.post call, so patching it is sufficient:

from unittest.mock import patch

@patch('django_forwardemail.services.requests.post')
def test_something(mock_post):
    mock_post.return_value.status_code = 200
    mock_post.return_value.json.return_value = {}
    # ... call code that sends email ...

Best Practices

  1. Restrict admin access: API keys live in the database and are editable through the Django admin.

  2. Separate environments: Use different API keys for dev/staging/production.

  3. Monitor logs: Set up logging to track email sending success/failure.

  4. Test configurations: Verify email sending works after configuration changes.

  5. Document settings: Keep track of which sites use which configurations.