Django ForwardEmail
A Django package for integrating with the ForwardEmail API service, providing multi-site email configuration and Django email backend support.
Features
- Multi-site Support: Configure different ForwardEmail settings for each Django site
- Django Email Backend: Drop-in replacement for Django's default email backend
- Service Class: Direct API integration for custom email sending logic
- Admin Interface: User-friendly Django admin interface for managing configurations
- Type Safety: Full type hints and mypy compatibility
- Comprehensive Logging: Debug-friendly logging for troubleshooting
Requirements
- Python 3.10+
- Django 4.2+
Installation
pip install django-forwardemail
Quick Start
1. Add to Django Settings
Add django_forwardemail to your INSTALLED_APPS:
INSTALLED_APPS = [
# ... other apps
'django.contrib.sites', # Required for multi-site support
'django_forwardemail',
]
# Optional: Set ForwardEmail as your default email backend
EMAIL_BACKEND = 'django_forwardemail.backends.ForwardEmailBackend'
# Optional: Configure ForwardEmail API base URL (defaults to https://api.forwardemail.net)
FORWARD_EMAIL_BASE_URL = 'https://api.forwardemail.net'
2. Run Migrations
python manage.py migrate django_forwardemail
3. Configure in Django Admin
- Go to Django Admin → Sites → Sites and ensure you have your site configured
- Go to Django Admin → Django ForwardEmail → Email Configurations
- Create a new configuration with:
- Site: Select your site
- API Key: Your ForwardEmail API key
- From Email: Default sender email address
- From Name: Default sender name
- Reply To: Default reply-to address
Usage
Using Django's Email System
Once configured, you can use Django's standard email functions:
from django.core.mail import send_mail, EmailMultiAlternatives
# Simple text email
send_mail(
'Subject here',
'Here is the message.',
'from@example.com',
['to@example.com'],
fail_silently=False,
)
# HTML email with text fallback
msg = EmailMultiAlternatives(
'Subject here',
'Plain text version of the message.',
'from@example.com',
['to@example.com']
)
msg.attach_alternative('<h1>HTML version</h1><p>of the message.</p>', "text/html")
msg.send()
Using the Service Class Directly
For more control, use the ForwardEmailService directly:
from django_forwardemail.services import ForwardEmailService
from django.contrib.sites.models import Site
# Get the current site
site = Site.objects.get_current()
# Send simple text email
response = ForwardEmailService.send_email(
to='recipient@example.com',
subject='Test Email',
text='This is a test email.',
site=site,
)
# Send HTML email with text fallback
response = ForwardEmailService.send_email(
to='recipient@example.com',
subject='HTML Test Email',
text='This is the plain text version.',
html='<h1>HTML Email</h1><p>This is a <strong>test</strong> email.</p>',
site=site,
)
# Send with custom from/reply-to
response = ForwardEmailService.send_email(
to='recipient@example.com',
subject='Custom Sender Email',
text='This email has custom sender info.',
from_email='custom@example.com',
reply_to='support@example.com',
site=site,
)
Using in Django Views
Here's how to integrate email sending in your Django views:
from django.shortcuts import render
from django.contrib import messages
from django.http import HttpResponseRedirect
from django_forwardemail.services import ForwardEmailService
from django.contrib.sites.shortcuts import get_current_site
def contact_view(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
message = request.POST.get('message')
try:
# Send notification email
ForwardEmailService.send_email(
to='admin@example.com',
subject=f'Contact Form: {name}',
text=f'From: {name} <{email}>\n\nMessage:\n{message}',
html=f'''
<h2>Contact Form Submission</h2>
<p><strong>From:</strong> {name} <{email}></p>
<p><strong>Message:</strong></p>
<p>{message}</p>
''',
reply_to=email,
request=request, # Automatically detects site
)
messages.success(request, 'Your message has been sent!')
return HttpResponseRedirect('/contact/')
except Exception as e:
messages.error(request, f'Failed to send message: {str(e)}')
return render(request, 'contact.html')
Template-based Emails
For complex emails, use Django templates:
from django.template.loader import render_to_string
from django_forwardemail.services import ForwardEmailService
def send_welcome_email(user, request):
# Render email templates
text_content = render_to_string('emails/welcome.txt', {
'user': user,
'site_name': 'Your Site',
})
html_content = render_to_string('emails/welcome.html', {
'user': user,
'site_name': 'Your Site',
})
# Send email
ForwardEmailService.send_email(
to=user.email,
subject='Welcome to Your Site!',
text=text_content,
html=html_content,
request=request,
)
Async Email Sending with Celery
For high-volume applications, use Celery for async email sending:
from celery import shared_task
from django_forwardemail.services import ForwardEmailService
from django.contrib.sites.models import Site
@shared_task
def send_email_async(to, subject, text, html=None, site_id=1):
"""Send email asynchronously using Celery."""
try:
site = Site.objects.get(id=site_id)
ForwardEmailService.send_email(
to=to,
subject=subject,
text=text,
html=html,
site=site,
)
return f"Email sent successfully to {to}"
except Exception as e:
return f"Failed to send email: {str(e)}"
# Usage in views
def some_view(request):
# Queue email for background processing
send_email_async.delay(
to='user@example.com',
subject='Background Email',
text='This email was sent in the background.',
site_id=request.site.id,
)
Multi-site Configuration
For multi-site setups, create separate EmailConfiguration objects for each site:
from django.contrib.sites.models import Site
from django_forwardemail.models import EmailConfiguration
# Configure for site 1
site1 = Site.objects.get(domain='site1.example.com')
EmailConfiguration.objects.create(
site=site1,
api_key='your-api-key-1',
from_email='noreply@site1.example.com',
from_name='Site 1',
reply_to='support@site1.example.com',
)
# Configure for site 2
site2 = Site.objects.get(domain='site2.example.com')
EmailConfiguration.objects.create(
site=site2,
api_key='your-api-key-2',
from_email='noreply@site2.example.com',
from_name='Site 2',
reply_to='support@site2.example.com',
)
API Reference
ForwardEmailService
send_email(**kwargs)
Send an email through the ForwardEmail API.
Parameters:
to(str): Recipient email addresssubject(str): Email subject linetext(str): Plain text email contentfrom_email(str, optional): Sender email address (uses config default if not provided)html(str, optional): HTML email contentreply_to(str, optional): Reply-to email address (uses config default if not provided)request(HttpRequest, optional): Django request object for site detectionsite(Site, optional): Django Site object (auto-detected if not provided)base_url(str, optional): ForwardEmail API base URL
Returns:
Dict[str, Any]: API response from ForwardEmail
Raises:
ImproperlyConfigured: If site configuration is missingException: If email sending fails
EmailConfiguration Model
Django model for storing ForwardEmail configurations per site.
Fields:
site: ForeignKey to Django Siteapi_key: ForwardEmail API keyfrom_email: Default sender email addressfrom_name: Default sender namereply_to: Default reply-to addresscreated_at: Timestamp when createdupdated_at: Timestamp when last updated
Settings
FORWARD_EMAIL_BASE_URL
Default: 'https://api.forwardemail.net'
The base URL for the ForwardEmail API. You typically don't need to change this unless you're using a custom ForwardEmail instance.
EMAIL_BACKEND
Set this to 'django_forwardemail.backends.ForwardEmailBackend' to use ForwardEmail as your default email backend.
MAILERS
Django 6.1 deprecates EMAIL_BACKEND in favour of MAILERS and removes it in Django 7.0. On Django 6.1+ you can configure the backend as a mailer alias instead:
# settings.py
MAILERS = {
'default': {
'BACKEND': 'django_forwardemail.backends.ForwardEmailBackend',
},
}
Extra backend options go under OPTIONS, and unknown options are reported as an InvalidMailer error. Both settings are supported; use EMAIL_BACKEND if you also need to run on Django 6.0 or earlier.
Development
Running Tests
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=django_forwardemail
Code Quality
# Format code
black django_forwardemail/
# Lint code
flake8 django_forwardemail/
# Type checking
mypy django_forwardemail/
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for your changes
- Ensure all tests pass
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Documentation: https://django-forwardemail.readthedocs.io/
- Issues: https://github.com/Pacficient-Labs/django-forwardemail/issues
- ForwardEmail Documentation: https://forwardemail.net/docs
Changelog
1.0.1
- Allow
send_mail()and the email backend to work without an explicitly providedsiteorrequestby falling back to the current site (and then the first site) - Development status promoted to Production/Stable
1.0.0 (2025-08-30)
- STABLE RELEASE - Production-ready ForwardEmail integration
- Multi-site email configuration support with automatic site detection
- Django email backend integration with full compatibility
- ForwardEmail API service class with comprehensive error handling
- Django admin interface for easy configuration management
- Full backward compatibility with existing Django email patterns
- Comprehensive test suite covering Django 4.2+ and Python 3.10+
- Modern type hints and logging integration
- Extensive documentation and usage examples
- Automated CI/CD pipeline with PyPI publishing
Metadata
Release files for django-forwardemail 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_forwardemail-1.0.2.tar.gz | 40.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_forwardemail-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 62.6 kB
Release files / django_forwardemail-1.0.2.tar.gz
| Download URL | django_forwardemail-1.0.2.tar.gz |
|---|---|
| Size | 40.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
562642736303bd714058aaf44bd8ffb6029a4d099378a31433c3cfeebcadcadc
|
|
BLAKE2b-256 checksum How to use checksums |
c8644b7560e0bcdc7c6684ec9aa1b8b94e182ceb0c73af6ad4b1860631bda6e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency logRelease files / django_forwardemail-1.0.2-py3-none-any.whl
| Download URL | django_forwardemail-1.0.2-py3-none-any.whl |
|---|---|
| Size | 21.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e49d1521da5237cf4ee99515ef13e9988c1fb88d44b6887680f7d15d7f8b3ef1
|
|
BLAKE2b-256 checksum How to use checksums |
70ceb7cc2c8f826c287d67f40cfc03d9e5f96c4967d627df2214492c76abddaa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency log