Lesson 23 / 23 · Commentail 1.4.1
What we will configure
Ordinary site mail and Commentail notifications can use different accounts and even different delivery methods. We will keep the site’s current transport and give Commentail a separate SMTP account. The alternative keeps the site on SMTP and sends Commentail mail through the Resend API.
Starting point
Start with Commentail 1.4.1, a working site and its ordinary mail. For an existing project, begin with 2. Add Commentail to an existing Wagtail site. SMTP credentials and sender authorization are covered in 6. Send email through SMTP; environment variables in 5. Environment variables without key confusion. For upgrades, use 20. Upgrade and restore Commentail.
1. Supply separate credentials
In a local macOS/Linux terminal set the values below, replacing every YOUR_… placeholder with your provider’s values. An app password can differ from the web login password. The provider must authorize the sender. Use dedicated practice credentials.
export COMMENTAIL_SMTP_HOST='YOUR_SMTP_HOST'
export COMMENTAIL_SMTP_USERNAME='YOUR_SMTP_USERNAME'
export COMMENTAIL_SMTP_PASSWORD='YOUR_SMTP_APP_PASSWORD'
export COMMENTAIL_FROM_EMAIL='Comments <comments@YOUR_VERIFIED_DOMAIN>'
For PowerShell use $env:NAME = 'value', following the environment lesson. On a server, supply the same variables to the website and worker through its existing secret configuration. Django does not load .env automatically. Do not print the dictionary containing the password.
2. Add settings beside COMMENTAIL
Append this separate dictionary in the project’s active settings file. Keep the existing EMAIL_* and ANYMAIL settings for ordinary site mail. Do not nest it inside COMMENTAIL or add hostname sections: this route applies to every Site.
import os
# Keep the site's existing EMAIL_* and ANYMAIL settings.
COMMENTAIL_EMAIL = {
"BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"OPTIONS": {
"host": os.environ["COMMENTAIL_SMTP_HOST"],
"port": 587,
"username": os.environ["COMMENTAIL_SMTP_USERNAME"],
"password": os.environ["COMMENTAIL_SMTP_PASSWORD"],
"use_tls": True,
"use_ssl": False,
"timeout": 20,
},
"FROM_EMAIL": os.environ["COMMENTAIL_FROM_EMAIL"],
}
This uses STARTTLS on port 587. For implicit TLS use the provider’s port, usually 465, use_ssl=True and use_tls=False. Never enable both modes. All separate-account connection fields are explicit here: omitted SMTP fields inherit the corresponding EMAIL_* defaults. A backend path alone does not isolate credentials.
OPTIONS contains constructor arguments, so use host and username, not EMAIL_HOST and EMAIL_HOST_USER. Nonempty options require an explicit BACKEND. The backend and fail_silently option names are reserved.
3. Check both routes
Restart the website and worker. From the site environment and the directory containing manage.py, run the check and direct probe, replacing the address with a permitted mailbox you control:
python manage.py check
python manage.py test_commentail_email --to recipient@example.org
check validates structure and imports without contacting the provider. The probe immediately sends one plain-text message without a comment, token or queue job; it runs even when Send emails is off. Success means backend acceptance, so separately inspect the sender, inbox and spam. Then exercise the site’s ordinary mail feature, such as a password reset for your own test account: it should retain the original provider and sender.
4. Complete the notification flow
Enable Send emails for the practice Site. Submit allowed guest text in a private browser, then run python manage.py process_commentail. Check email confirmation by button, publication notice after approval of a pending comment with Publication emails on, the separate edit link, rejection with Rejection emails on, and a direct published reply notice with unsubscribe. Use Review all for a rejected new guest comment: a Reject rule in Apply rules refuses it before creating mail. Follow the complete notification flow.
Inspect Comments → Email delivery, provider events and received messages. The queue and schedule remain unchanged; 8. Schedule email processing and cleanup.
Alternative: Resend API for Commentail
Use this instead of the SMTP dictionary above when the site should retain its SMTP route. Prepare an authorized domain and a Resend sending key using 7. Use the Resend API through Django Anymail. Install Anymail in the site environment and add "anymail" once to the existing INSTALLED_APPS:
python -m pip install django-anymail==15.2
Supply COMMENTAIL_RESEND_API_KEY=YOUR_API_KEY and COMMENTAIL_FROM_EMAIL to both processes. Replace only COMMENTAIL_EMAIL with the dictionary below; keep the site’s shared EMAIL_BACKEND and SMTP settings:
import os
# Add "anymail" once to the existing INSTALLED_APPS.
COMMENTAIL_EMAIL = {
"BACKEND": "anymail.backends.resend.EmailBackend",
"OPTIONS": {
"api_key": os.environ["COMMENTAIL_RESEND_API_KEY"],
"requests_timeout": 20,
},
"FROM_EMAIL": os.environ["COMMENTAIL_FROM_EMAIL"],
}
api_key and requests_timeout are Anymail parameters, not universal SMTP options. Respect the account’s sender and recipient restrictions. Restart both processes and repeat both probes and the full flow. See the Anymail 15.2 Resend backend and per-instance settings.
Failures, shared delivery and cleanup
An explicit backend failure never switches Commentail to shared delivery. Check the exception class, variable presence without printing values, authorized sender and provider events. Fixing credentials does not automatically revive a failed job: use the permitted admin retry. Link deadlines do not extend. Retrying an uncertain send can produce duplicates.
Pending jobs use the active route at processing time, even when created earlier. To return to shared delivery, remove COMMENTAIL_EMAIL or set it to {}, then restart both processes. A sender-only override is also valid: COMMENTAIL_EMAIL = {"FROM_EMAIL": "Comments <comments@example.org>"}. After practice, revoke only dedicated test keys or app passwords after restoring the intended production configuration.
Choose a sign-in method or comment as a guest below.