๐ง Django Email Sender Version 2ยถ
Django Email Sender is a lightweight and highly customisable utility for sending emails in Django using rich templates and a clean, chainable API.
Whatโs New in v2.0ยถ
Version 2.0 brings powerful new features that make your email workflows smarter and more robust:
โ Email Delivery Tracking
Get immediate feedback on whether an email was sent successfully or failed, with access to error messages if something goes wrong.
๐ Field Management (Preserve/Clear)
Fine-grained control over email fieldsโclear or preserve specific fields even after setting them.
๐ Logging and Database Integration
Easily hook into your logging system to record email activityโlog to file, console, or even a database.
๐ง New method introduced to Chainable API
Enhanced method chaining to improve developer ergonomics and reduce boilerplate.
โจ Featuresยถ
Rich HTML and plain text templates
Chainable email building methods
Custom function to format your logging
Additional methods for fine grain control (v2.0+)
Logging and database integration (v2.0+)
๐งพ Changelogยถ
v2.0
Added support for logging (file/database)
Added delivery status tracking with error feedback
Introduced methods to clear or preserve fields dynamically
Improved documentation and method chaining flexibility
Ability to get metadata for a sent email
See you payload before or after your email has been set
and so much more
It provides a clean, reusable, and chainable utility class for sending emails in Django, supporting both HTML and plain text templates, dynamic context injection, and flexible usage โ whether used directly, via subclassing, or abstracted into functions.
Why Use This?ยถ
While Django already provides a way to send emails, it can become verbose and repetitive. EmailSender abstracts the boilerplate and lets you send templated emails fluently.
Upgrading from Version 1ยถ
Version 2 introduces powerful new features โ with zero breaking changes. Your current integration will continue to work as expected!
What is New in Version 2ยถ
โ Integrated Logging & Database Support
Log email activity to a file or database with customisable logging levels (
debug,info,warning,error).
๐งผ Advanced Field Management
Auto-reset fields after sending
Clear specific fields or all fields
Preserve chosen fields across sends
๐ Selective Logging with Inclusion/Exclusion Rules
Choose exactly which fields to log (
log only) and which to exclude (log exclude) โ giving you full control over what gets logged.
๐ Custom Log Range
Define a log range to specify precisely what data is captured in logs, improving privacy and debugging clarity.
๐ง Smarter Error Handling
Now with field-level and error messages for easier debugging and localisation.
โ No breaking changes โ Drop-in upgrade, full backwards compatibility.
๐ฆ Whatโs New in Version 2 contโd?ยถ
Version 2 of Django Email Sender brings major upgrades while maintaining full backwards compatibility. Itโs smarter, more flexible, and much more powerful.
๐ Custom Logger Integration
Plug in your own logger for full control over log output and formatting.
Easily inherit from the base abstract model to create a custom log model.
Save logs to the database or file via the
EmailLoggerintegration.
๐ Auto-Reset After Sending
Automatically reset all email fields after sending withauto_reset=Trueto prevent accidental resends.๐ Preserve Specific Fields
Usepreserve_fieldsto retain selected fields even after an auto-reset.๐งผ Individual Field Clearing
Clear only what you need with new methods likeclear_subject(),clear_context(),clear_to(), and more.โ๏ธ Inclusion/Exclusion Logging
Choose exactly which fields to log (log_only) and which to exclude (log_exclude) for fine-grained control.๐ Custom Log Range
Define a logging range to specify precisely what data is logged, improving privacy and audit clarity.๐ EmailSenderLogger Class
A dedicated logger class to manage logging behaviour, output types, and integration points.๐งฐ More Utility Methods
Version 2 introduces several new helper methods to simplify template handling, payload inspection, metadata access, and more.Check the full documentation for all available methods and usage examples.
Featuresยถ
๐ ๏ธ Custom logger integration
Plug in your own logger for full control over log formatting and output. Easily log email activities to files or databases.
๐๏ธ Database logging support
Integrate email logs directly into your database via the
EmailLogger, with easy-to-configure log models.
๐ Chainable API
Fluent, easy-to-use API for configuring email attributes โ e.g.,
.to(),.from_address(),.subject(),.context(), etc.
๐ Auto-reset
Automatically reset all fields after sending an email, keeping your instance clean for reuse.
๐งผ Field clearing and preservation
Clear individual fields (e.g., subject, recipients, templates) or preserve them between email sends.
๐งผ logging including inclusion/exclusion
Chose what fields you would like to log (e.g., subject, recipients, templates)
๐จ HTML and plain text templates
Send both HTML and plain-text emails with rich template support for flexibility.
๐งฉ Lightweight & easy integration
Simple to integrate into any Django project without unnecessary complexity.
๐งฑ Clean architecture & folder structure
Encourages good code practices with reusable components and clear folder organization.
๐งฌ Subclassing & functional abstractions
Extensible through subclassing or functional abstractions for maximum flexibility.
๐งช Testable and extendable
Designed with testability in mind, making it easy to write unit and integration tests.
Sample code comparisionยถ
Sample Code Comparison: Version 1 vs Version 2
Version 1 (Classic Usage)
# Without auto-reset and custom logger (classic usage)
from django_email_sender.email_sender import EmailSender
(
EmailSender.create()
.from_address("no-reply@example.com")
.to(["test@example.com"])
.with_subject("Welcome Email")
.with_context({"username": "John"})
.with_html_template("welcome.html", folder_name="welcome")
.with_text_template("welcome.txt", folder_name="welcome")
.send()
)
๐ Version 2 โ Demo: Custom Logger & Database Logging
import logging
from django_email_sender.email_sender import EmailSender
from django_email_sender.email_logger import EmailSenderLogger
from django_email_sender.email_sender_constants import LoggerType, EmailSenderConstants
# For database logging, import your custom model inheriting from EmailBaseLog:
from <your_app>.models import YourCustomEmailLog
# ---------------------------
# Example: models.py
# ---------------------------
from django.db import models
from django_email_sender.models import EmailBaseLog
class YourCustomEmailLog(EmailBaseLog):
# You can add any custom fields or methods if needed
pass
# ---------------------------
# Example: views.py
# ---------------------------
# Optional: custom formatter for logger messages
def my_custom_formatter(exception: Exception, trace: str) -> str:
return f"[CUSTOM ERROR] {str(exception)} | Trace: {trace}"
# Set up your own logger
logger = logging.getLogger("email_sender")
# Start the logging session
email_sender = (
EmailSenderLogger.create()
.start_logging_session()
.enable_verbose() # Enables verbose output for detailed logging
.config_logger(logger, LoggerType.DEBUG) # Plug in your custom logger
.add_email_sender_instance(EmailSender.create()) # Attach the EmailSender instance
.add_log_model(YourCustomEmailLog) # Use your custom DB model for logs
.enable_email_meta_data_save() # Enable to save to db
.from_address("no-reply@example.com")
.to("jackie@example.com")
.add_new_recipient("mj@example.com")
.add_new_recipient("ba@example.com")
.add_new_recipient("eu@gmail.com")
.add_new_recipient("scott_mccall@example.com")
.with_context({"promo_code": "12345"})
.with_subject("Promo code for our loyal customers")
.with_html_template("test_email.html", "sender")
.with_text_template("test_email.txt", "sender")
.send()
)
# Access the payload (for debugging or logging)
payload = email_sender.payload
# Access email meta-data (headers, timestamps, etc.)
meta_data = email_sender.email_meta_data
โ Notes:
Make sure your logger is configured in settings.py.
Your custom model must inherit from EmailBaseLog.
You can override or extend the base model with your own fields.
๐ก Tip:
This is just a basic example โ additional configuration options like preserve_fields, auto_reset, clear_*() methods, etc. See the documentation for details
๐ Minimal Example โ Custom Logger Only (No DB Integration)ยถ
A minimal example that shows off just the custom logging integration without the database part. This is great for users who want to plug in a logger quickly without setting up a database model.
import logging
from django_email_sender.email_sender import EmailSender
from django_email_sender.email_logger import EmailSenderLogger
from django_email_sender.email_sender_constants import LoggerType
# Optional: custom formatter for your log output
def my_custom_formatter(exception: Exception, trace: str) -> str:
return f"[CUSTOM LOG] {str(exception)} | Traceback: {trace}"
# Set up your logger (must be configured in Django settings)
logger = logging.getLogger("email_sender")
# Start logging session
email_sender = (
EmailSenderLogger.create()
.start_logging_session()
.enable_verbose() # Verbose logging for detailed trace
.config_logger(logger, LoggerType.INFO)
.set_custom_formatter(my_custom_formatter)
.add_email_sender_instance(EmailSender.create())
.from_address("noreply@example.com")
.to("user@example.com")
.with_subject("Welcome!")
.with_html_template("emails/welcome.html", "sender")
.with_text_template("emails/welcome.txt", "sender")
.send()
)
# Optionally access logging details
print(email_sender.payload)
print(email_sender.email_meta_data)
โก Highlights
No custom model setup required.
Plug-and-play logging with any Python logger.
Add your own formatter to control log message output.
Great for development or production environments where full DB logging isnโt needed.
What is EmailSender and EmailSenderLogger and are they needed?ยถ
EmailSender is a module that allows you to send customisable emails with rich templates. It abstracts the multiple steps needed to send an email and enables you to do so in a quick, easy, and chainable manner.
While EmailSender does support sending emails, it lacks several functionalities:
No tracking of the sending process, making debugging difficult
No logging capabilities
No visibility of payloads (pre-send or post-send)
No access to metadata
No database interaction
No delivery tracking
Why is EmailSenderLogger needed?ยถ
Although EmailSender has recently been upgraded with new featuresโsuch as clearing specific fields, resetting values, and preserving data after sendingโit still focuses solely on sending emails. As a result, it doesnโt provide methods to track or log email operations.
This is where EmailSenderLogger comes in.
Itโs a lightweight wrapper that extends EmailSender with powerful features:
Integrated logging support
Optional database logging
Email report summaries (e.g., sent vs not sent)
Real-time access to the email payload during construction
Full metadata retrieval after the email is sent
Preview support for both HTML and plain text templates
And much more
โ ๏ธ Note
EmailSenderLoggerdoes not include a logger or database by default. You must inject these via the provided public methods.
If you choose not to supply a logger or database, thatโs fineโEmailSenderLogger will still work, inheriting all functionality from EmailSender.
To use it, simply power EmailSenderLogger with an instance of EmailSender:
email_sender = (
EmailSenderLogger.create()
.add_email_sender_instance(EmailSender.create()) # must be powered by the instance of EmailSender
.from_address("noreply@example.com")
.to("user@example.com")
.with_subject("Welcome!")
# rest of the chain here
# ...
.send()
)
# do something with `email_sender` if you want
What if I am not using the advanced features of EmailSenderLogger?ยถ
Thatโs completely fine. If you donโt require logging or database integration, you can continue using EmailSender directly.
However, EmailSenderLogger is a subclass of EmailSender, meaning it fully supports all core email-sending features. If you prefer to use EmailSenderLogger (e.g., for future extensibility), simply initialise it with an instance of EmailSender.
EmailSenderLogger.create().add_email_sender_instance(EmailSender.create() or EmailSender())
Unless you explicitly opt-in to the enhanced features (e.g., by starting a logging session), EmailSenderLogger behaves just like EmailSender.
Available Methodsยถ
Method Description
EmailSender methodsยถ
- create()
- from_address(email)
- to(recipients)
- with_subject(subject)
- with_context(context)
- with_text_template(folder_name="folder-name-here", template_name="template-name-here.txt")
- with_html_template(folder_name="folder-name-here", template_name="template-name-here.html")
- with_headers(headers)
- clear_from_email() # Added in version 2
- clear_to_email() # Added in version 2
- clear_subject() # Added in version 2
- clear_context() # Added in version 2
- clear_to_email() # Added in version 2
- clear_html_template() # Added in version 2
- clear_text_template() # Added in Version 2
- clear_from_email() # Added in Version 2
- clear_all_fields() # Added in Version 2
- send()
EmailSender Class API Referenceยถ
๐จ create()ยถ
Factory method โ Instantiates and returns an
EmailSenderobject.
๐ค from_address(email)ยถ
Sets the senderโs email address.
noreply@yourdomain.com). This is the email address located in your settings.py file variable e.g โEMAIL_HOST_USERโ
๐ฅ to(recipients)ยถ
Sets the recipient(s) of the email.
recipients: A string or list of strings with one or more email addresses.
New in version 2, no longer accepts a list. Use
.add_new_recipient()to add multiple recipients
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_subject() # Clears the subject field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_subject()
Note
The.to(...)method accepts either a single email string or a list of email addresses.
However, the list format is supported only for backwards compatibility.
If you pass a list like["first_email@example.com", "second_email@example.com"], only the first email ("first_email@example.com") will be used.โ ๏ธ Important: Passing a list only works when using
EmailSender.
If youโre usingEmailSenderLogger, passing a list will raise an error. In that case, always pass a single string email address to.to(...).To add multiple recipients, use the
.add_new_recipient()method instead.โ ๏ธ Do not use
.to(...)to add multiple emails โ it will overwrite theto_emailfield rather than append to it.
๐ with_subject(subject)ยถ
Sets the subject line of the email.
subject: A string for the emailโs subject.
๐ง with_context(context)ยถ
Provides the context dictionary for rendering templates.
context: Optional. A dictionary containing variables that can be used in both HTML and text templates. This method is only necessary if your templates require dynamic content (variables) to be rendered.
๐ with_text_template(folder_name="folder-name-here", template_name="template-name-here.txt")ยถ
Specifies the plain text template.
Iffolder_nameis omitted, defaults toemails_templates/.
๐ with_html_template(folder_name="folder-name-here", template_name="template-name-here.html")ยถ
Specifies the HTML version of the email template.
Iffolder_nameis omitted, defaults toemails_templates/.
๐งพ with_headers(headers)ยถ
Optional method to add custom email headers.
headers: A dictionary of headers (e.g.{"X-Custom-Header": "value"}).
โ๏ธ clear_subject()ยถ
Clears the subject field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the subject field without affecting other fields in the chain.
๐งณ clear_context()ยถ
New in version 2.
Clears the context field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the context field without affecting other fields in the chain
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_context() # Clears the context field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_context() # Clears the context field from the chain
๐จ clear_to_email()ยถ
New in version 2.
Clears the recipient field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the recipient field without affecting other fields in the chain.
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_to_email() # Clears the recipient field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_to_email()
๐จ clear_from_email()ยถ
New in version 2.
Clears the sender email field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the sender email field without affecting other fields in the chain
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_from_email() # Clears the sender email field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_from_email()
๐งโ๐ป clear_html_template()ยถ
New in version 2.
Clears the HTML template field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the HTML template field without affecting other fields in the chain.
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_html_template() # Clears the HTML template field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_html_template()
๐ clear_text_template()ยถ
New in version 2.
Clears the text template field to its default empty value. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears the text template field without affecting other fields in the chain.
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_text_template() # Clears the text template field from the chain
# or it can be cleared directly from the instant method
email_sender.clear_text_template()
๐ clear_all_fields()ยถ
New in version 2.
Clears all fields to their default empty values. This method is optional and can be called as part of a method chain. Itโs only relevant if the object has been instantiated and used as a chain. Calling this method clears all fields without affecting the rest of the method chain or the logger if added.
from django_email_sender.email_sender import EmailSender
# instantialize the Email sender
email_sender = (
EmailSender.create()
email_sender.from_address(from_email)
.to(user.email)
.with_subject(subject)
.with_context({"username": 'John'})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
.clear_all_fields() # Clears all fields in the chain without but leaves fields like logger, etc intact
)
# or it can be cleared directly from the instant method
email_sender.clear_all_fields()
๐ฌ send(auto_reset=False)ยถ
Sends the email using the provided configuration and templates.
Optional parameters New in version 2.
auto_reset If set to True, all fields will be cleared after the email is sent. Default is False.`
EmailSenderLogger Class API Referenceยถ
๐จ create()ยถ
Factory method โ Instantiates and returns an
EmailSenderLoggerobject.
๐ค add_email_sender_instance(email_sender_instance)ยถ
Sets the core
EmailSenderinstance that will be used to send emails.
๐ฅ to(recipients)ยถ
The ability to add multiple recipients via a list has been removed and replaced with
add_new_recipient.
This method now only accepts a string. To add multiple recipients useadd_new_recipientmethod
๐ฅ add_new_recipient(recipient)ยถ
New in version 2.
Accepts a string and adds it to the set of recipients. To add multiple recipients, call this method repeatedly. The method uses a set to ensure that recipient names are unique.
๐ with_subject(subject)ยถ
Sets the subject line of the email.
๐ง with_context(context)ยถ
Sets the context dictionary for dynamic rendering of email templates.
๐ with_text_template(folder_name, template_name)ยถ
Specifies the plain text template to use.
Iffolder_nameis omitted, defaults toemails_templates/.
๐ with_html_template(folder_name, template_name)ยถ
Specifies the HTML version of the email template.
Iffolder_nameis omitted, defaults toemails_templates/.
๐ with_headers(headers)ยถ
Optional method to add custom email headers (as a dictionary).
๐งฉ set_custom_formatter(custom_formatter)ยถ
Adds a custom error formatter to customise how exceptions and traces are logged.
๐ config_logger(logger: Logger, log_level: LoggerType)ยถ
Integrates an external Python logger and sets its log level.
Logger Configurationยถ
๐งพ To customise how and at what level email sending is logged, use the config_logger(). The levels can be added manually or used with LoggerType constants provided.
from django_email_sender.email_sender_constants import LoggerType
email_logger.config_logger(my_logger, LoggerType.INFO)
Constant |
Description |
|---|---|
|
Standard informational messages |
|
Non-critical issues worth noting |
|
Errors encountered during sending |
|
Verbose output for development/debugging |
๐ add_log_model(log_model: EmailBaseLog)ยถ
Attaches a custom model for database email logging.
The model provided must inherit from theabstractbase modelEmailBaseLogThe module path :from django_email_sender.models import EmailBaseLog
๐งช to_debug(message)ยถ
Logs a message at the
DEBUGlevel.
โน๏ธ to_info(message)ยถ
Logs a message at the
INFOlevel.
โ ๏ธ to_warning(message)ยถ
Logs a message at the
WARNINGlevel.
โ to_error(message)ยถ
Logs a message at the
ERRORlevel.
๐ enable_verbose()ยถ
Enables verbose logging (e.g., shows additional trace and context information).
๐ disable_verbose()ยถ
Disables verbose mode and limits logs to essential information.
Logging Control Methodsยถ
Use the following methods to manage logging dynamically during the email sending flow.
start_logging_session() ๐ยถ
Starts the logging session and returns
selffor chaining.
Useful if you want to enable logging partway through your workflow.Note: If a logger has been added, you must call this method โ otherwise, no information will be logged, and youโll see the following output in your logs:
[2025-05-10 17:41:37,136] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-10 17:41:37,136] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-10 17:41:37,136] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-10 17:41:37,136] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
๐ stop_logging_session()ยถ
Completely disables further logging and ends the session.
โธ๏ธ pause_logging()ยถ
Temporarily pauses logging without clearing state.
โธ๏ธ resume_logging()ยถ
Resumes logging after a
pause_logging()call.
๐ฌ is_email_sent (property)ยถ
Returns
Trueif the last email was successfully sent, otherwiseFalse.
๐ข email_delivery_count (property)ยถ
Returns the number of successfully delivered emails in the current session.
๐ฆ payload (Property)ยถ
Returns the email payload dictionary. Useful for auditing and testing.
๐งพ email_meta_data (Property)ยถ
Returns meta information such as timestamps, recipients, and status.
๐งฎ return_successful_payload() (Not chainable)ยถ
Returns a copy of the
_field_changesdictionary, which logs what fields were changed and when.
This ensures the original audit trail remains unmodified.
๐ง set_traceback(show_traceback: bool, method_tracing: bool = False )ยถ
method_tracing: show exactly which methods were called during your email building process,
set_traceback()is your friend. It provides a step-by-step breakdown of the chained method calls that lead up to sending an email. Note, this must be set toTrue, default isFalseshow_traceback: shows you a traceback error including the point where the error originated.
๐ log_only_fields(*fields)ยถ
Restricts logging to specific fields (e.g., only
subjectorto).
Useful for minimising log verbosity.
๐ซ exclude_fields_from_logging(*fields)ยถ
Excludes specific fields from being logged, even if logging is enabled.
๐ reset_field_logging_filters()ยถ
Clears all field-based filters (
log_only_fieldsandexclude_fields_from_logging) and resets to default logging behaviour.
๐ง enable_email_meta_data_save(save_to_db=True)ยถ
Enables metadata saving for each email sent. If
save_to_db=True, the metadata will also be saved to the database via the configured model.
๐ง return_successful_payload()ยถ
Returns the email payload for logging, but only if the email was successfully processed.
Additional API Notesยถ
๐ Shared Methods Between EmailSenderLogger and EmailSender
๐ Since EmailSenderLogger is a wrapper around EmailSender, it inherits many of the same methods. For more detailed explanations, refer to the EmailSender API section.
Using Model Constants to Minimise Errorsยถ
๐จ To reduce the risk of typos and improve code clarity, both EmailSender and EmailSenderLogger support the use of constants via the EmailSenderConstants enum.
Instead of hardcoding strings like "from_email" or "subject" when specifying fields, use the enums for safer, auto-completable references.
โ ๏ธ Note: These enums must be used with .value, as they are instances of Enum. This can then be passed in
methods like exclude_fields_from_logging or log_until_field
EmailSenderConstants Fields ๐ ยถ
Constant |
Field Name |
Description |
|---|---|---|
|
|
The senderโs email address |
|
|
The recipientโs email address |
|
|
The subject line of the email |
|
|
The HTML template used to render the email body |
|
|
The plain text version of the email body |
|
|
A dictionary of variables used in template rendering |
|
|
Optional custom email headers |
|
|
A list of recipient email addresses |
|
|
Unique identifier for the email instance |
๐ Logging-specific use (with EmailSenderLogger)
from django_email_sender.email_sender_constants import EmailSenderConstants
email_sender_with_logging = EmailSenderLogger.create().start_logging_session()
email_sender_with_logging.exclude_fields_from_logging(
EmailSenderConstants.Fields.CONTEXT.value,
EmailSenderConstants.Fields.HEADERS.value,
)
This approach ensures consistency across your codebase and provides a single source of truth for all field and method references related to EmailSender and EmailSenderLogger.= โadd_new_recipientโ
EmailSender and EmailSenderLogger Methodsยถ
Core Functions
Name |
Type |
Chainable |
Description |
Defined In |
|---|---|---|---|---|
|
Method |
โ |
Factory method to instantiate the class |
Both |
|
Method |
โ |
Set recipient(s) |
Both |
|
Method |
โ |
Set the subject of the email |
Both |
|
Method |
โ |
Set template context |
Both |
|
Method |
โ |
Attach plain text template |
Both |
|
Method |
โ |
Attach HTML template |
Both |
|
Method |
โ |
Add custom headers |
Both |
|
Method |
โ |
Sends the email |
Both |
|
Method |
โ |
Clears the subject field |
Both |
|
Method |
โ |
Clears the context dictionary |
Both |
|
Method |
โ |
Clears the recipient(s) |
Both |
|
Method |
โ |
Clears the from address |
Both |
|
Method |
โ |
Clears the HTML template |
Both |
|
Method |
โ |
Clears the text template |
Both |
|
Method |
โ |
Clears all email-related fields |
Both |
Database Accessยถ
Name |
Type |
Chainable |
Description |
Defined In |
|---|---|---|---|---|
|
Method |
โ |
Inject an EmailSender into the logger wrapper |
|
|
Method |
โ |
Attach a model to persist email logs |
|
|
Method |
โ |
Enables saving of email meta to the database |
|
Logging and Verbose Functionsยถ
Name |
Type |
Chainable |
Description |
Defined In |
|---|---|---|---|---|
|
Method |
โ |
Only log specific fields |
|
|
Method |
โ |
Exclude fields from logging |
|
|
Method |
โ |
Enable logging mode |
|
|
Method |
โ |
Stop logging and finalise log object |
|
|
Method |
โ |
Temporarily stop logging changes |
|
|
Method |
โ |
Resume logging after a pause |
|
|
Method |
โ |
Enable verbose logging |
|
|
Method |
โ |
Disable verbose logging |
|
|
Method |
โ |
Set a formatter for custom log formatting |
|
|
Method |
โ |
Enable stack trace logging on errors and the order methods were called |
|
|
Method |
โ |
Configure logger object and the log level |
|
Logging Level Methodsยถ
Name |
Type |
Chainable |
Description |
Defined In |
|---|---|---|---|---|
|
Method |
โ |
Changes the level to info |
|
|
Method |
โ |
Changes the level to debug |
|
|
Method |
โ |
Changes the level to warning |
|
|
Method |
โ |
Changes the level to error |
|
Properties and Metadataยถ
Name |
Type |
Chainable |
Description |
Defined In |
|---|---|---|---|---|
|
Property |
โ |
Property that returns if the email was sent |
|
|
Property |
โ |
Property that returns number of deliveries |
|
|
Property |
โ |
Returns metadata of sent email |
|
|
Property |
โ |
Property returning the full internal state |
|
|
Method |
โ |
Returns a copy of the payload (audit fields) |
|
Explanation of Categories:ยถ
Core Functions: The main email sending functionality, like setting recipients, subject, context, templates, headers, and clearing fields.
Database Access: Methods that deal with interacting with a database, logging email details, saving email meta data, and configuring the logger.
Logging and Verbose Functions: Methods that manage the logging session, verbosity, and custom log formatting.
Properties and Metadata: Methods that return properties related to the email state, including whether it was sent, the delivery count, and email metadata.
Code Style Tipsยถ
๐ Formatting long method chainsยถ
When chaining multiple methods, breaking the chain onto separate lines can cause syntax errors unless you use an escape character (\). However, this approach can be difficult to read. A cleaner solution is to wrap the chain in parentheses.
๐น Using backslashes (\)ยถ
This works but can become harder to read as the chain grows:
# Assume that you passed in a user class
EmailSender.create()\
.from_address(from_email)\
.to([user.email])\
.with_subject(subject)\
.with_context({"username": user.username})\
.with_text_template(text_registration_path, folder_name="emails")\
.with_html_template(html_registration_path, folder_name="emails")\
.send()
๐น Using parentheses (recommended)ยถ
This method is cleaner, more readable, and less error-prone:
# Assume that you passed in a user class
EmailSender.create()
.from_address(from_email)
.to([user.email])
.with_subject(subject)
.with_context({"username": user.username})
.with_text_template(text_registration_path, folder_name="emails")
.with_html_template(html_registration_path, folder_name="emails")
.send()
Installation via Pypiยถ
django-email-sender is a Django package that allows you to send emails using customizable templates, with easy-to-use methods for setting the sender, recipients, subject, and context.
To install the package:
pip install django-email-sender
For more details, visit the PyPI page.
Requirementsยถ
Python 3.10+
This library uses Structural Pattern Matching, introduced in Python 3.10, via thematch-casesyntax.
Why Python 3.10?ยถ
One of the key features used in this project is the match-case syntax, which offers a more readable and expressive way to handle complex conditional logic.
Example Usage:ยถ
def log_level_handler(level: str) -> str:
match level.lower():
case "debug":
return "Logging in debug mode"
case "info":
return "Standard info logging"
case "warning":
return "Warning: Check your configuration"
case "error":
return "Error occurred during processing"
case _:
return "Unknown logging level"
This syntax is not available in versions prior to Python 3.10, so attempting to run the library on an earlier version will raise a SyntaxError.
Dependencies:
List of required dependencies (install withpip install -r requirements.txt)
Compatibilityยถ
This package has been tested against Django 5.2 (the latest version at the time of release) and is known to work with versions 3.2 and above.
โ ๏ธ Compatibility with Django 6.x and beyond is not yet guaranteed. If youโre using a future version, proceed with caution and consider opening an issue if anything breaks.
Logger and Database Integrationยถ
๐งฉ EmailSendervia EmailSenderLogger, supports optional integration with both logging and database persistence for email sending events.
Logging Integrationยถ
๐ชต EmailSenderLogger does not provide a built-in logger. You must configure and pass your own logger (e.g., using Pythonโs built-in logging module). If no logger is provided, EmailSenderLogger will still function as expected โ silently and without log output.
Key Points:
If you pass a logger, it will be used as-is.
EmailSenderLogger does not modify logger levels, handlers, or formatters.
If you do not set a logger level (e.g., INFO, DEBUG), the logger will be ignored.
If no logger is configured, email sending proceeds normally, without logging. If you do not configure your formatting then default spacing will be used which may or may not align depending if you are using different levels
config_loggerยถ
The config_logger method is entirely optional. You only need to call it if you want to enable logging for EmailSenderLogger. If not called, EmailSenderLogger will skip all logging operations and focus solely on sending the email โ no setup required, no extra overhead.
To enable logging, you must also call:
start_logging_session()
This ensures that logging starts. Without calling this, the logger will not log anything.
This setup gives you full flexibility:
Enable logging when you need visibility into the email-sending process.
Skip logging for quicker or simpler email sends.
EmailSenderadapts to your needs โ whether you prefer a fully monitored pipeline or a fast, lightweight send.
How to configure the Loggerยถ
๐ Once the user has configured a logger, it can be passed using the config_logger chain method.
Use LoggerType to minimise errors and ensure consistency across your implementation.
See Logger Configuration for a full list of supported types and usage examples.
The parameters available for config_logger are:
Parameter |
Type |
Description |
|---|---|---|
|
|
The logger instance provided by the user. |
|
|
The log level to listen to (e.g., โinfoโ, โerrorโ). |
๐ Notesยถ
If no logger is set,
EmailSenderLoggerwill not log anythingAdvanced users may customise logging heavily using the
custom_formatter.Casual users can simply provide a logger and rely on default behaviour.
Users not interested in logging can simply skip the set logger setup and proceed with sending emails.
Example Setting up a simple Logger with EmailSenderLoggerยถ
In this section, weโll configure a Python simple logger and demonstrate how to integrate it with EmailSenderLogger.
import logging
from django_email_sender.email_sender import EmailSender
from django_email_sender.email_logger import EmailSenderLogger
from django_email_sender.email_sender_constants import LoggerType
# Step 1: Set up a basic logger
logger = logging.getLogger("email_sender_logger")
# Optional: Add a console handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("[%(levelname)s] %(message)s"))
logger.addHandler(console_handler)
# Step 2: (Optional) Define a custom formatter
def my_custom_formatter(exception: Exception, trace: str) -> str:
return f"Error occurred: {str(exception)} | Traceback: {trace}"
# Step 3: Use the logger with EmailSender
email_sender = (
EmailSenderLogger.create().start_logging_session()
.enable_verbose()
.config_logger(logger, LoggerType.DEBUG) # logger added
.add_email_sender_instance(EmailSender.create())
.set_custom_formatter(my_custom_formatter) # only use this option when you are passing in an optional custom formatter
.from_address("no-reply@example.com")
.to("no-reply@example.com")
.with_subject("test subject")
.with_html_template("test_email.html")
.with_text_template("test_email.txt")
.send()
)
๐ Whats Exactly Is Happening Here?ยถ
Step |
What |
|---|---|
1 |
We create a simple logger using Pythonโs built-in |
2 |
(Optional) We define a |
3 |
We chain |
Enabling the Loggerยถ
๐ By default, even if youโve configured your logger using methods such as config_logger() or **logging will not begin until you explicitly call**start_logging_session()`. This ensures that there is no accidental logging unless you want to log.
If you forget to call start_logging_session(), the system will inform you through repeated debug messages like the following:
[2025-05-11 22:20:27,875] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-11 22:20:27,875] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-11 22:20:27,875] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-11 22:20:27,875] DEBUG email_sender : [Logger Not Enabled: Logger is not enabled. Skipping logging. | category=LOGGER | status=NOT_ENABLED]
[2025-05-11 22:20:27,875] INFO email_sender : [Setting language safely...]
[2025-05-11 22:20:27,875] INFO email_sender : [Django is ready, activating language.]
To fix this, ensure you call the following after your logger setup:
.start_logging_session()
โ
Tip: You can stop the session anytime with stop_logging_session() or temporarily pause it with pause_logging() and resume_logging().
๐ ๏ธ Troubleshooting Tipยถ
If your logs are not being recorded or saved even after calling config_logger() and other setup methods, check that:
start_logging_session()has been called.The logging level (e.g.,
.to_info(),.to_debug()) matches your applicationโs verbosity.You have not paused logging with
pause_logging()without callingresume_logging()afterward.You are using a valid log model (if required), and
add_log_model()is set up properly if you want to log to thedatabase.
if you are still not seeing any logs? Look for
[Logger Not Enabled: Logger is not enabled. Skipping logging.]messages โ it means logging was configured but never started.
Reuse the Logger and Formatter Across Multiple Emailsยถ
If you have set up a custom logger or formatter setup and plan to send multiple emails, you donโt need to set them up every single time.
Instead, you can set them once when you create an instance of EmailSenderLogger, then reuse the same instance to send all your emails.
This way:
Your logger and formatter stay attached to the instance.
You avoid repetitive setup code.
Sending emails becomes much cleaner and faster.
Resetting or Reusing the Instance Cleanlyยถ
When reusing the same EmailSender or EmailSenderLogger instance to send multiple emails, certain fields (like recipients, subject, or context) may retain values from previous emails, especially if youโre not overriding them. Since youโre using a single instance of EmailSender or EmailSenderLogger if you are interested in logging or storing the data in a database then no errors will occur when calling the send method again, as these fields were set to required values earlier.
However, this can lead to issues where you might unintentionally send emails with the wrong template, subject, or even to the wrong recipient.
To avoid this, you have two options:
Use the
auto_resetflag: Set it toTrueto clear all fields before sending the next email.Manually clear specific fields: Call the appropriate
clear_<field_name>method (e.g.,clear_subject(),clear_context(),clear_all_fields(), etc.) to reset only the fields you need.
๐ Example Usageยถ
The example focuses on using the auto_reset flag to clear the fields but this can be done using the corresponding clear_<field_name> methodยถ
# Step 1: Create an instance and set the logger/formatter once
email_sender = EmailSenderLogger.create().config_logger(
logger=logger,
log_level=LoggerType.ERROR,
)
# Step 2: Send multiple emails using the same instance
# Example 1 - Send email 1
(
email_sender.from_address("no-reply@example.com")
.to("test@example.com")
.with_subject("Verify Your Email")
.with_context({"username": "John", "verification_code": "123456"})
.with_html_template("verification.html", folder_name="verification")
.with_text_template("verification.txt", folder_name="verification")
.send(auto_reset=True) # ensure a clean state
)
# Example 2 - Send email 2
# auto_reset is not set to true, default of False is used
(
email_sender.from_address("no-reply@example.com")
.to("test@example.com")
.with_subject("Welcome Email")
.with_html_template("welcome.html", folder_name="welcome")
.with_text_template("welcome.txt", folder_name="welcome")
.send()
)
# Example 3 - Send email 3
# Since `auto_reset` wasn't set in Example 2, and the user is not overriding the fields, the `welcome.html` and `welcome.txt` templates
# will be used for the third email, even though the subject ("Import Account Information") doesn't match the templates.
# This mismatch will result in the recipient receiving an email with irrelevant content.
# To avoid this issue, you should either manually clear the fields or use `auto_reset=True` to ensure a clean state.
(
email_sender.from_address("no-reply@example.com")
.to("test@example.com")
.with_subject("Meetup Information time and location")
.send(auto_reset=True) # ensure a clean state
)
โ All emails are sent using the same instance, with the logger and formatter already set up.
Tipยถ
If you are sending dozens of emails with the same logger and configuration, reusing
.clear_all_fields()might save you tiny performance overhead. But if youโre only sending 1โ2 emails each time, itโs easier to just create a fresh instance!
โจ Why This Mattersยถ
Performance: Avoids re-initialising the logger every time.
Cleaner Code: Reduces duplication and clutter.
Consistency: Ensures all emails follow the same logging/formatting rules.
โจ Key Points:ยถ
By default : EmailSenderLogger doesnโt log events to the console or file unless an error occurs
Custom logger: Use the
config_loggermethod if you need more control over logging, like logging to a file or an external service, See the flow of the email process, sending, delivery, errors, etcLogging configuration: You can configure your logger with handlers, formats, and levels as needed via the
settings.pywhich can be overriden when usingEmailSenderLogger
โ
Easy for beginners
โ
Powerful for advanced users
Tracing Method Chains and logging errors with set_tracebackยถ
๐งญ If you ever wanted to know exactly which methods were called during your email building process, set_traceback() is your friend. It provides a step-by-step breakdown of the chained method calls that lead up to sending an email.
This is especially useful for debugging complex chains, understanding the flow, or simply verifying that everything is working in the expected order.
Why Use Method Tracing?ยถ
When working with complex chains of method calls or deeply nested logic, it can be difficult to understand the exact flow of execution. Method tracing provides a powerful way to gain visibility into whatโs happening under the hood.
Debug complex chains with greater ease
Understand execution flow step-by-step
Verify correct method ordering and dependencies
Enabling chain tracingยถ
To enable method tracing, you must:
Set
method_tracingparameter in theset_tracebackmethod toTrueCall
enable_verbose()set the level to
Debugeither by theconfig_loggeror using the.to_debug()method
Chain tracing is only shown at the debug level and when enable_verbose mode is enabled to avoid overwhelming the user with too much information.
Hereโs how it works:ยถ
Method Chain Tracking
The method chain begins from the initial call (e.g.,create()) and continues through all chained methods liketo(),with_subject(), and so on. Each method gets logged in real-time.
โ๏ธ Behind the Scenes (Optional Detail for Power Users)
Internally, show_traceback() appends each method name and its arguments to a trace log as the chain is built. This is particularly helpful in:
Debugging method order issues
Catching repeated or conflicting method calls
Understanding flow when extending or contributing to the library
๐งช Example Output from set_traceback(method_tracing=True)
When set_traceback(method_tracing=True) is enabled, youโll see something like this in your logs or console (depending on your logger configuration):
\[TRACE] Method Chain Started:
โ create()
โ start\_logging\_session()
โ config\_logger()
โ add\_email\_sender\_instance()
โ from\_address()
โ to()
โ with\_subject()
โ with\_text\_template()
โ send()
Each arrow (โ) represents a method call in your builder chain. This gives you full visibility into whatโs been run before .send() is triggered.
Verbose Debugging
Ifshow_traceback=Trueis enabled, stack trace information is included in the logs when an error happens. This includes:The sequence of method calls leading to the error.
Detailed information about the error (e.g., the specific line number, method name, and error message).
This allows you to track back to the root cause of an issue quickly and effectively.
Example of an error traceback:
Consider an email sending process like this:
# Assume the neccessary modules have been imported
email = EmailSenderLogger.create() \
.start_logging_session()\
.enable_verbose()\
.add_email_sender_instance(EmailSender())\
.config_logger(logger, LoggerType.DEBUG)\
.set_traceback(method_tracing=True, show_traceback=True)\
.to("recipient@example.com") \
.with_subject("Test Email") \
.with_html_template("invalid_test_template.html") \
.... other methods here
.send()
Now, letโs assume that thereโs an error while sending due to an invalid template, and youโve set show_traceback=True. The logged traceback might look like this:
[DEBUG][METHOD_TRACE] Method Chain Started: EmailSenderLogger._get_environment() โ EmailSenderLogger._create_meta_data() โ EmailSenderLogger._validate_template() โ EmailSenderLogger._send()
--- Error Traceback ---
File "email_sender.py", line 145, in _send
raise TemplateNotFoundError("HTML template not found")
TemplateNotFoundError: HTML template not found
Breakdown of what happens:
The log shows the exact method calls that were made in the process.
The error is flagged, and the traceback reveals where the failure occurredโ in the
_send()method, specifically due to aTemplateNotFoundError.You can now see exactly which method caused the problem and what the error was, making it easier to debug.
Why is this useful?
Debugging Complex Chains: If youโre chaining many methods together, itโs often hard to track down where an error happens.
show_traceback=Trueensures that you know exactly where to look.Detailed Logging: You get more than just a โmethod failedโ message; you get a full traceback that includes method names, file locations, and specific error messages, which can greatly speed up debugging.
You can also do something like this to ensure that you always have a traceback
# Assume the necessary modules have been imported
import os
email_sender = (
EmailSenderLogger.create()
.start_logging_session()
.config_logger(logger)
.add_email_sender_instance(EmailSender)
.from_address("dev@example.com")
.to("debug@example.com")
.with_subject("Dev Mode Email")
.with_text_template("dev_template.txt", "emails")
)
# Automatically enable tracing in dev
if os.getenv("ENV") == "development":
email_sender.set_traceback(True, True)
email_sender.enable_verbose()
email_sender.send()
Turning on Verbose Modeยถ
When using EmailSenderLogger, depending on the log level youโve chosen (info, warning, error, or debug), youโll see information corresponding to those levels. By default, the logger doesnโt show detailed step-by-step information to avoid overwhelming you. Instead, it focuses on providing the most relevant details for debugging.
However, if you want to view more detailed information, you can enable verbose logging with the enable_verbose() method. When enabled, youโll see extra details that would normally be hidden. Note that enable_verbose will show the additional information based on the log level youโve selected. For example:
In
debugmode, youโll see logs from all levels:debug,info,warning, anderror.In
infomode, youโll seeinfo,warning, anderrorlogs.In
warningmode, youโll seewarninganderrorlogs.In
errormode, youโll only seeerrorlogs.
To use verbose logging, simply chain the enable_verbose() method. To disable verbose mode, use the disable_verbose() method.
Example usage:
# assume that neccessary modules have been imported
email_sender = (
EmailSenderLogger.create()
.start_logging_session()
.config_logger(logger, LoggerType.INFO)
.enable_verbose() # Enable verbose mode
.add_email_sender_instance(EmailSender)
.from_address("test@example.com")
.to("recipient@example.com")
.send()
)
Setting up an advanced loggerยถ
In the above example we set up a very limited logger that doesnโt involve the user configure the settings.py file, however that
logger has several limited.
โ ๏ธ Limitations:
Logger might duplicate messages:
Django automatically configures logging early during startup, which may cause the logger to propagate messages up to Djangoโs root logger.
This could result in messages being printed twice: once by your
console_handlerand once by Djangoโs default logger.
No control through Django settings:
You canโt easily change logging behaviour (e.g., send email errors, write to file, silence logs in production) through Djangoโs
LOGGINGconfiguration if you manually build loggers everywhere.
Handler added multiple times:
If the code runs multiple times (e.g., Django imports modules multiple times), you could end up attaching multiple handlers, which causes duplicate log messages.
To configure your email_sender_logger properly through Djangoโs LOGGING settingsยถ
EmailSender via EmailSenderLogger supports flexible logging integration. To enable logging, configure a logger in your Django settings.py (or your project settings) like this:
# -------------------------------------------------------------------
# settings.py
#--------------------------------------------------------------------
LOGGING = {
"version": 1,
"disable_existing_loggers": False, # required so Django's default logging still works
"formatters": {
'right_indented': {
'()': 'your-app-path-to-this-file.RightIndentedFormatter', # Update with the correct path to your custom formatter
'format': '[%(asctime)s] %(levelname)-8s %(name)-13s: [%(message)s]',
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "right_indented", # Use 'right_indented' formatter here
},
"file": {
"class": "logging.FileHandler",
"filename": "emails.log", # Specify your log file name here
"formatter": "right_indented", # Use 'right_indented' formatter here
},
},
"loggers": {
"email_sender": { # Logger name used by EmailSender or whatever name you chose
"handlers": ["console", "file"], # Both handlers for console and file
"level": "DEBUG", # Set log level to DEBUG or as needed
"propagate": False, # This prevents propagation to parent loggers
},
},
}
Now create a utils.py file (or choose a name that suits your project) inside your app folder, and define your custom formatter there. This ensures proper indentation and structure for logging output, as shown below.
[2025-05-08 06:49:56,087] INFO email_sender : Some information
[2025-05-08 06:49:56,087] DEBUG email_sender : Some Debug message
[2025-05-08 06:49:56,195] WARNING email_sender : Some Waringin
[2025-05-08 06:49:56,195] Error email_sender : Some Waringin
# -------------------------------------------------------------------
# utils.py
#--------------------------------------------------------------------
import logging
class RightIndentedFormatter(logging.Formatter):
def __init__(self, fmt=None, datefmt=None, style='%'):
super().__init__(fmt, datefmt, style)
def format(self, record):
# Add a custom indent to the beginning of the log message
original_message = super().format(record)
return f" {original_message}" # 4 spaces or any amout of space you want
Note:
Make sure to link formatter file in the LOGGER with the settings.py and now whenever
you send an email it will nicely be displayed in a formatted setting.
Advanced Tip: Rotate Log Files Automaticallyยถ
For larger applications, itโs recommended to rotate your log files to avoid growing indefinitely. You can modify the file handler to automatically create a new log file each day:
from logging.handlers import TimedRotatingFileHandler
"handlers": {
"file": {
"class": "logging.handlers.TimedRotatingFileHandler",
"filename": "django.log",
"when": "midnight", # Create a new file every midnight
"backupCount": 7, # Keep last 7 days of logs
"formatter": "standard",
},
},
This will keep your logs clean without manually deleting old files.
Advanced Logger Usageยถ
EmailSenderLogger allows you to monitor and capture detailed information while using the EmailSender. You can configure it in various ways depending on how much control or verbosity you need.
The most straightforward usage is to configure the logger with a specific log level โ such as ERROR, WARNING, INFO, or DEBUG โ and let it automatically log events to the console or a file.
๐น First Approachยถ
Set up the Logger with a Log Level
You can initialise a logger and attach it to EmailSenderLogger to record activity at a chosen level. This is useful for capturing general usage, errors, or debugging info.
EmailSenderLogger.create().config_logger(
logger=logger,
log_level=LoggerType.ERROR
)
Second Approachยถ
.log_only_fields(*fields)ยถ
Use the .log_only_fields() method to log only specific fields youโre interested in.
This gives you fine-grained control and avoids cluttering your logs with unnecessary data.
To use this method, import EmailSenderConstants and pass in the fields youโd like to track.
See the full list of fields under EmailSenderConstants.
from django_email_sender.constants import EmailSenderConstants
EmailSenderLogger.create()
.start_logging_session()
.config_logger(logger, LoggerType.DEBUG) # start of debug
.log_only_fields(
EmailSenderConstants.Fields.SUBJECT.value,
EmailSenderConstants.Fields.TO_EMAIL.value
)
๐น Third Approachยถ
.exclude_fields_from_logging(*fields)
Sometimes, you might want to log everything except certain fields.
The .exclude_fields_from_logging() method allows you to omit specific fields from being recorded in your logs.
This is especially useful when logging sensitive data such as emails or templates, or just to reduce noise.
from django_email_sender.constants import EmailSenderConstants
EmailSenderLogger.create()
.start_logging_session()
.config_logger(logger, LoggerType.DEBUG) # start of debug
.exclude_fields_from_logging(
EmailSenderConstants.Fields.HTML_TEMPLATE.value,
EmailSenderConstants.Fields.CONTEXT.value
)
โ ๏ธ Note: Do not use
.log_only_fields()and.exclude_fields_from_logging()together. These methods are mutually exclusiveโuse one or the other depending on your logging preference.
๐ Change Levels Dynamically During Loggingยถ
During the logging process, you may wish to change levels to capture different aspects of whatโs happening. For instance, you might start in DEBUG mode and then switch to INFO, WARNING, or ERROR depending on what data you want to capture.
You can do this using .to_debug(), .to_info(), .to_warning(), and .to_error() โ all without restarting or reconfiguring the logger.
๐งช Example 1 โ Switching Levels While Chainingยถ
# Assume Logger and EmailSender have been imported and linked
email_sender = (
EmailSenderLogger.create().start_logging_session()
.config_logger(logger, LoggerType.DEBUG) # Start at DEBUG
.add_email_sender_instance(EmailSender)
.from_address("test@example.com")
.to_info() # Switch to INFO
.to("jackie@example.com")
.to_warning() # Switch to WARNING
.with_subject("test subject")
.with_html_template("test_email.html", "sender")
.with_text_template("test_email.txt", "sender")
.to_error() # Final switch to ERROR
.send()
)
๐งช Example 2 โ Changing Levels with config_loggerยถ
You can also switch levels inline by re-calling .config_logger().
from django_email_sender.email_sender import EmailSender, LoggerType
import logging
logger = logging.getLogger("email_sender")
(
EmailSenderLogger.create()
.config_logger(logger, LoggerType.DEBUG)
.from_address("no-reply@example.com")
.to("jtest@example.com")
.config_logger(logger, LoggerType.INFO)
.with_subject("test")
.config_logger(logger, LoggerType.WARNING)
.with_context({"username": "John", "code": "123456"})
.with_headers({"X_HEADER": "some header"})
.config_logger(logger, LoggerType.ERROR)
.with_html_template("test_email.html", folder_name="sender")
.with_text_template("test_email.txt", folder_name="sender")
.send()
)
Cancel or Pause Logging Mid-Flowยถ
๐ If you only want to log certain parts of your flow, you can stop or pause the logger at any point.
Use the following methods:
start_logging_session() โ starts the logging session.
stop_logging_session()โ permanently stops logging for the current flow.pause_logging()โ temporarily halts logging.resume_logging()โ resumes logging after a pause.
Example: Stopping the Logging Sessionยถ
# Assume Logger and EmailSender have been imported and linked
email_sender = (
EmailSenderLogger.create().start_logging_session()
.config_logger(logger, LoggerType.DEBUG)
.add_email_sender_instance(EmailSender)
.from_address("test@example.com")
.to("jackie@example.com")
.stop_logging_session() # Logging ends here
.with_subject("test subject")
.with_html_template("test_email.html", "sender")
.with_text_template("test_email.txt", "sender")
.send()
)
Anything after the .to() field will not be captured in the logs.
Example: Pausing and Resuming Loggingยถ
email_sender = (
EmailSenderLogger.create().start_logging_session()
.config_logger(logger, LoggerType.DEBUG)
.add_email_sender_instance(EmailSender)
.from_address("test@example.com")
.to("jackie@example.com")
.pause_logging() # Temporarily pause
.with_subject("test subject") # Not logged
.resume_logging() # Logging resumes
.with_html_template("test_email.html", "sender")
.with_text_template("test_email.txt", "sender")
.send()
)
Key points
Provides advanced way to log or monitor or your data
Allows you to log or exclude fields
Allows you to an
end pointwhere it can then run up toDoes not force you to use these advanced featues, you can just as easily set up with
config_loggerand let run or use no logger at allIt is easier for beginners to use and allows
advancedusers more control over their logging
Sample Logging Reportยถ
๐ Below is a sample snippet from the logging output generated when an email is processed using EmailSenderLogger with logging enabled.
This report captures a comprehensive summary of the email sending processโincluding details like recipients, templates used, status, time taken, and previews of the message content. Itโs particularly useful for debugging, auditing, or tracking email delivery during development or production.
[2025-05-08 06:49:56,758] INFO email_sender : [________________________________________________________________________
[2025-05-08 06:49:56,758] INFO email_sender : [
[2025-05-08 06:49:56,758] INFO email_sender : [ '**Email Sent Process Summary Logs**']
[2025-05-08 06:49:56,758] INFO email_sender : [________________________________________________________________________
[2025-05-08 06:49:56,758] INFO email_sender : [Email ID : '8679bf20ada7055179feefc730305e28d8070b2844e74b6152b83a6aa3f3e205'
[2025-05-08 06:49:56,759] INFO email_sender : [Timestamp : 2025-05-08 05:49:56.757213+00:00
[2025-05-08 06:49:56,759] INFO email_sender : [Language code sent in : en-us
[2025-05-08 06:49:56,760] INFO email_sender : [Subject : apple
[2025-05-08 06:49:56,760] INFO email_sender : [From : en@gmail.com
[2025-05-08 06:49:56,760] INFO email_sender : [To : peter@gmail.com
[2025-05-08 06:49:56,760] INFO email_sender : [Additional Recipients : ['bin@example.com', 's@exmaple.com', 'jake@gmailcom', 'peter@gmail.com']
[2025-05-08 06:49:56,761] INFO email_sender : [Total Recipients : 5
[2025-05-08 06:49:56,761] INFO email_sender : [Template Used (HTML) full path : C:\full\path\to\template\email_templates\emails_templates\sender\test_email.html
[2025-05-08 06:49:56,762] INFO email_sender : [Template Used (Text) full path : C:\full\path\to\template\email_templates\test_email.txt
[2025-05-08 06:49:56,763] INFO email_sender : [HTML short file name : test_email.html
[2025-05-08 06:49:56,763] INFO email_sender : [Text short file name : test_email.txt
[2025-05-08 06:49:56,763] INFO email_sender : [Attachments Added : 'None'
[2025-05-08 06:49:56,763] INFO email_sender : [Environment : 'Development'
[2025-05-08 06:49:56,765] INFO email_sender : [Time Taken : 0.00 seconds
[2025-05-08 06:49:56,766] INFO email_sender : [Status : Failed to send email
[2025-05-08 06:49:56,766] INFO email_sender : [Emails delivered successfully : 0
[2025-05-08 06:49:56,766] INFO email_sender : [Text Preview : hi, { username} please verify your email by clicking this link { code }..
[2025-05-08 06:49:56,767] INFO email_sender : [HTML Preview : Verify Your Email Verify Your Email Address Hi { username }, Pleas...
[2025-05-08 06:49:56,768] INFO email_sender : [Email format : multipart/alternative (HTML + plain text)
[2025-05-08 06:49:56,768] INFO email_sender : [________________________________________________________________________
Database Integrationยถ
EmailSenderLogger allows users to optionally persist email metadata to a database. This is useful for audit logs, diagnostics, and history tracking.
๐ Note: EmailSenderLogger does not create or manage any database tables. You must define your own log model, and explicitly opt in to database logging.
Requirements:
You must create your own model that inherits from EmailBaseLog.
The model must be passed as a class, not an instance, using .add_log_model().
You must explicitly enable database logging using .enable_email_meta_data_save().
If no valid model is added, no data will be saved
๐ Example: Custom Email Log Modelยถ
# models.py
from django_email_sender.models import EmailBaseLog
class CustomEmailLog(EmailBaseLog):
# Optional: Add custom fields
request_id = models.CharField(max_length=100, null=True, blank=True)
environment = models.CharField(max_length=20, default='production')
def __str__(self):
return f"{self.recipient} | {self.subject} | {self.status}"
Run migrations
python manage.py makemigrations
python manage.py migrate
๐ ๏ธ Usage in Codeยถ
from django_email_sender.email_sender import EmailSender
from django_email_sender.email_logger import EmailSenderLogger
from "your-app".models import CustomEmailLog
email_sender = ( EmailSenderLogger.create().start_logging_session()
.add_log_model(CustomEmailLog) # customEmailLog
.enable_email_meta_data_save() # enable saving to the
# Rest of the code here .
# .....
.send()
)
๐ Payload & Metadata Accessยถ
Payload Inspectionยถ
EmailSenderLogger provides structured access to the full email payload in json via the .payload property.
This enables developers to inspect, log, or persist detailed data related to any email being sent. The payload is dynamically constructed, allowing you to access and inspect it either after sending the email or as you build each field during the setup process.
โ What the Payload Includes:ยถ
from_emailโ Email address of the senderto_emailโ List or string of recipient addressessubjectโ Subject line of the emailbody_htmlโ HTML version of the messagebody_textโ Plain-text version of the messagecontextโ Context dictionary used to render the templateheadersโ Any custom headers attached to the message
โ ๏ธ Why This Method Is Not Chainableยถ
Unlike other methods in EmailSender, this one is not chainable. This is intentional.
The purpose of the method is to return a structured payload containing dynamic email data such as from_email, to_email, subject, body_html, etc. Since EmailSenderLogger assumes you may need to inspect, log, or persist this data, the method returns the payload directly instead of returning the instance (self).
This design ensures developers have immediate access to the email data when they need it โ whether during construction or after sending to either inspect or use as part of the application. Making it chainable would require additional steps to extract the data, which would reduce clarity and usability.
๐ฆ Example usage :ยถ
payload = email_sender.payload
print(payload)
# {
# "from_email": "admin@example.com",
# "to_email": ["user@example.com"],
# "subject": "Welcome to Our Platform",
# "body_html": "full/path/to/html/template/",
# "body_text": "full/path/to/text/template/",
# "context": {"username": "john_doe"},
# "headers": {"X-Custom-Header": "value"}
or
# use the data somewhere in your application
HTML Email Template Exampleยถ
django-email-sender supports sending beautiful HTML emails using Django templates.
This example shows a verification email template that you can use out of the box or modify to suit your needs.
๐๏ธ Save this as: templates/emails_templates/emails/verify_email.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Verify Your Email</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 30px auto;
background-color: #fff;
padding: 20px;
border-radius: 8px;
}
.code {
font-size: 32px;
font-weight: bold;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>Verify Your Email Address</h1>
<p>Hi {{ username }},</p>
<p>Please verify your email address by entering the following code:</p>
<div class="code">{{ verification_code }}</div>
<p>If you didn't request this, you can safely ignore this email.</p>
</div>
</body>
</html>
Plain Text & Multi-part Email Supportยถ
django-email-sender supports both plain text and multi-part (HTML + text) emails. This ensures emails are readable in all clients, including those that donโt support HTML.
๐ Plain Text Email Exampleยถ
๐๏ธ Save this as: templates/emails_templates/emails/verify_email.txt
Hi {{ username }},
Please verify your email address by entering the following code:
{{ verification_code }}
If you didn't request this, you can safely ignore this email.
## Usage Example
๐จ Multi-part Email (HTML + Plain Text) usage
Use both .with_text_template() and .with_html_template() together to send a multi-part email:
from django_email_sender.email_sender import EmailSender
EmailSender.create()
.from_address("noreply@example.com")
.to(["user@example.com"])
.with_subject("Please verify your email")
.with_context({
"username": user.username,
"verification_code": "123456"
})
.with_html_template("verify_email.html", folder_name="emails")
.with_text_template("verify_email.txt", folder_name="emails")
.send()
โจ This approach helps you keep your email logic clean and makes templates easy to design or preview.
Explanation:ยถ
.from_address("no-reply@example.com"): Specifies the senderโs email address..to("recipient@example.com"): Specifies the recipientโs email address..with_subject("Welcome!"): The subject of the email..with_context({"username": "John"}): Context for the email templates, allowing dynamic insertion of values (e.g., the recipientโs name)..with_text_template("welcome.txt", folder_name="emails"): The path to the text-based email template. Here, we specify the folder name (emails) where the template is stored. If no folder name is provided, it defaults toemail_templates/..with_html_template("welcome.html", folder_name="emails"): The path to the HTML-based email template. Similarly, you can specify the folder name (emails) for this template..send(): Sends the email.
Subclassingยถ
You can also subclass the EmailSender class to create more specific types of emails.
Example: Password Reset Emailยถ
class PasswordResetEmail(EmailSender):
def __init__(self, user):
super().__init__()
self.user = user
def build(self):
return self\
.from_address("no-reply@example.com")\
.to([self.user.email])\
.with_subject("Reset Your Password")\
.with_context({"username": self.user.username, "reset_link": generate_reset_link(self.user)})\
.with_text_template("reset_password.txt", folder_name="emails")\
.with_html_template("reset_password.html", folder_name="emails")
Usage:ยถ
PasswordResetEmail(user).build().send()
Here, the PasswordResetEmail class uses reset_password.txt and reset_password.html templates from the emails folder.
Function-Based Abstractionsยถ
๐ ๏ธ For a functional approach, you can also wrap EmailSender in specific functions to handle common email use cases.
Example: Sending a Verification Emailยถ
def send_verification_email(user):
html_verification_path = "verification/verification.html"
text_verification_path = "verification/verification.txt"
subject = "Verify Your Email"
from_email = "no-reply@example.com"
return EmailSender.create()\
.from_address(from_email)\
.to([user.email])\
.with_subject(subject)\
.with_context({
"username": user.username,
"verification_link": generate_verification_link(user)
})\
.with_text_template(text_verification_path, folder_name="emails")\
.with_html_template(html_verification_path, folder_name="emails")\
.send()
Example: Sending a Registration Emailยถ
def send_registration_email(user):
html_registration_path = "registration/registration.html"
text_registration_path = "registration/registration.txt"
subject = "Welcome to the Platform!"
from_email = "no-reply@example.com"
return EmailSender.create()\
.from_address(from_email)\
.to([user.email])\
.with_subject(subject)\
.with_context({"username": user.username})\
.with_text_template(text_registration_path, folder_name="emails")\
.with_html_template(html_registration_path, folder_name="emails")\
.send()
Advantages of this Approach:ยถ
Keeps your logic functional and simple: Itโs straightforward to use and easy to test.
Keeps your email templates modular and easy to override: Templates are organized in subfolders (e.g.,
registration,verification), making them easier to manage.Clean and maintainable codebase: You donโt have to subclass
EmailSendereach time, reducing complexity.
Templatesยถ
๐ Templates must reside inside a dedicated email_templates/ directory, which should exist inside your Django template directory.
This folder can contain your own structure to help organise different types of emails. For example:
Example
project/
โโโ templates/
โ โโโ email_templates/
โ โโโ registration/
โ โโโ registration.html
โ โโโ registration.txt
When calling with_html_template() or with_text_template(), you can provide the subfolder and filename like so:
EmailSender.create()
.with_html_template("registration.html", folder_name="registration")
.with_text_template("registration.txt", folder_name="registration")
You must have both an .html and .txt version of the email template. These are required for rich content and email client compatibility.
Configuring the Template Directoryยถ
๐ EmailSender allows you to easily configure the location of template directories used by the app, including email templates. By default, EmailSender will look for templates in a templates folder inside the base directory of your project. However, if youโd like to customize the location, you can do so using the MYAPP_TEMPLATES_DIR setting in your Django projectโs settings.py.
Default Behaviourยถ
By default, EmailSender will look for templates in the following directory:
{BASE_DIR}/templates/emails_templates/
Where:
BASE_DIRis the root directory of your Django project (wheremanage.pyis located).templatesis the default directory where EmailSender expects to find your templates.emails_templatesis the subdirectory where email-related templates should be stored.
Customizing the Template Directory Pathยถ
If youโd like to customize the template directory location, you can define the MYAPP_TEMPLATES_DIR setting in your settings.py file.
Steps to Override:ยถ
Open your
settings.pyfile.Define the
MYAPP_TEMPLATES_DIRsetting to point to your custom template folder.
Example:ยถ
# settings.py
BASE_DIR = Path(__file__).resolve().parent.parent
# Custom template directory location
MYAPP_TEMPLATES_DIR = BASE_DIR / "custom_templates"
In this example:
EmailSender will look for templates in
{BASE_DIR}/custom_templates/emails_templates/.If you do not define
MYAPP_TEMPLATES_DIR, EmailSender will use the default location:{BASE_DIR}/templates/emails_templates/.
How It Worksยถ
MYAPP_TEMPLATES_DIR: If defined, EmailSender uses this setting to locate the main template folder.Fallback: If
MYAPP_TEMPLATES_DIRis not defined, EmailSender falls back to the default location:{BASE_DIR}/templates.Email Templates: EmailSender looks specifically in the
emails_templates/subdirectory for email-related templates.
Example File Structure:ยถ
Default Setup:ยถ
my_project/
โ
โโโ templates/
โ โโโ emails_templates/
โ โโโ welcome_email.html
โ โโโ welcome_email.txt
Custom Setup (with MYAPP_TEMPLATES_DIR defined):ยถ
my_project/
โ
โโโ custom_templates/
โ โโโ emails_templates/
โ โโโ welcome_email.html
โ โโโ welcome_email.txt
Error Handlingยถ
If EmailSender cannot find the templates in the expected location, it will raise a error to let you know where the missing templates are expected.
If BASE_DIR is not defined in settings.py, an ImproperlyConfigured error will be raised to prompt you to define it.
Fallback Logicยถ
In case the MYAPP_TEMPLATES_DIR is not defined in settings.py, EmailSender will automatically fallback to the default template directory (templates) without requiring any extra configuration.
Conclusion
The MYAPP_TEMPLATES_DIR setting provides flexibility for users who prefer to store their templates in a custom location. By defining this setting in settings.py, users can control where the templates for EmailSender (including email templates) are stored, ensuring a smooth and configurable integration.
Putting It All Togetherยถ
This guide shows how to use django-email-sender in a Django project to send a verification email. This will not be using a logger. See the logger section See logger section on how to use EmailSender with a logger.
๐ Step 1: Virtual Environmentยถ
python -m venv venv
source venv/bin/activate.ps1
source venv/bin/activate # On Mac or linux use: venv\Scripts\activate
๐ฆ Step 2: Install Dependenciesยถ
pip install django django-email-sender
โ๏ธ Step 3: Create a Django Projectยถ
django-admin startproject config .
python manage.py startapp core
In config/settings.py, add 'core' to INSTALLED_APPS.
๐งฑ Step 4: Update Django Settingsยถ
Add the following settings to your settings.py file to configure the email backend and other email-related settings.
Email settings configurationsยถ
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com' # Replace with your email provider's SMTP server
EMAIL_PORT = 587 # Typically 587 for TLS
EMAIL_USE_TLS = True # Enable TLS encryption
EMAIL_HOST_USER = 'your-email@example.com' # Your email address
EMAIL_HOST_PASSWORD = 'your-email-password' # Your email password (or app password if using 2FA)
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # Default email to send from
Note replace
- smtp.example.com with your-email@example.com
- your-email-password with your actual email service provider's SMTP details
If you are using gmail to send emails then the setup would look like
# Email Backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# Email Settings for Gmail
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'your-email@gmail.com' # Your Gmail address
EMAIL_HOST_PASSWORD = 'your-app-password' # Use the generated app password (if 2FA is enabled)
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # Optional: Set default sender email (same as the one above)
Important Notes:ยถ
App Password: If you have two-factor authentication (2FA) enabled for your Gmail account, youโll need to create an App Password instead of using your regular Gmail password. You can generate it in your Google account settings.
TLS: Setting EMAIL_USE_TLS = True ensures that emails are sent securely over TLS encryption.
This configuration should allow you to send emails via Gmailโs SMTP server.
๐งฑ Step 4: Create Email Templatesยถ
Create the folder structure :
See HTML Email Template Example and Plain Text & Multi-part Email Support for how to create the files
Replace the folder
emailswithverificationDo the same with the file names
Then add the templates path in config/settings.py:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # This is where you add the line
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
๐งช Step 5: Add a Test Viewยถ
In core/views.py:
from django.http import HttpResponse
from django_email_sender.email_sender import EmailSender
def test_email_view(request):
(
EmailSender.create()
.from_address("no-reply@example.com")
.to(["test@example.com"])
.with_subject("Verify Your Email")
.with_context({ "username": "John", "verification_code": "123456"})
.with_html_template("verification.html", folder_name="verification")
.with_text_template("verification.txt", folder_name="verification")
.send()
)
return HttpResponse("Verification email sent!")
๐ Step 6: Wire Up URLsยถ
Create core/urls.py:
from django.urls import path
from .views import test_email_view
urlpatterns = [
path("send-verification-email/", test_email_view),
]
Then include it in config/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("core.urls")),
]
๐ Step 7: Run and Testยถ
python manage.py runserver
Open http://localhost:8000/send-verification-email/ in your browser and check your inbox!
๐ก Tipsยถ
You can subclass
EmailSenderfor different email types or simply wrap it in functions.Organise your templates by email type (
registration/,verification/, etc.)Subject and context are fully customisable.
Playing Around with Features Without Sending Emailsยถ
๐งช You can quickly test various features of EmailSenderLogger by setting up a simple view. Hereโs how:
Set up a sample view in your Django app.
Add this to your
settings.pyto log emails to the console:EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Add
<a-html-template>.htmland<a-test-template>.txttemplates to yourtemplatesdirectory to avoid template errors.html template: Your basic HTML structure.text template: Your basic text structure.
Now, all emails sent will be logged in the console only, without actually sending to real email addresses.
You can safely test features like:
Logging: Track the email sending process and potential errors.
Field Filtering: Choose which fields to log or save, such as subject, from email, or the context.
Metadata Storage: Save email metadata (like recipients, subject, timestamp) to a database for auditing purposes.
View the logging output in your console or test out these features.
What is django.core.mail.backends.console.EmailBackend?ยถ
It replicates the entire process of sending an email but logs it to the console instead of actually sending it to an address. This allows you to test the email-related features of EmailSenderLogger safely without sending real emails.
Example Viewยถ
# views.py
import logging
from django.http import HttpResponse
from your_app.models import CustomEmailLog
from django_email_sender.email_sender import EmailSender
from django_email_sender.email_logger import EmailSenderLogger
from django_email_sender.email_sender_constants import LoggerType, EmailSenderConstants
# Assume logger is configured in settings.py
logger = logging.getLogger("email_sender")
def test_email(request):
email_sender_logger = EmailSenderLogger.create()
(
email_sender_logger
.start_logging_session()
.enable_verbose()
.add_email_sender_instance(EmailSender())
.add_log_model(CustomEmailLog)
.enable_email_meta_data_save()
.config_logger(logger, LoggerType.DEBUG)
.log_only_fields(
EmailSenderConstants.Fields.CONTEXT.value,
EmailSenderConstants.Fields.SUBJECT.value,
EmailSenderConstants.Fields.FROM_EMAIL.value,
EmailSenderConstants.Fields.TEXT_TEMPLATE.value,
)
.reset_field_logging_filters()
.from_address("no-reply@gmail.com")
.to("to-reply@gmail.com")
.with_subject("Subject Line")
.with_html_template("test_email.html", "sender")
.with_text_template("test_email.txt", "sender")
.send()
)
print(email_sender_logger.payload)
print(email_sender_logger.email_meta_data)
print(email_sender_logger.return_successful_payload())
return HttpResponse("Email sent successfully!")
Key Points:
Test out features by logging email data to the console instead of sending actual emails.
This process allows you to safely check the logging, field filtering, metadata, payload, emails database storage features, etc without worrying about sending real emails to recipients.
Best Practicesยถ
Configure a Logger in Production
Always configure a logger for production environments to track errors and activities.
Ensure that your logger is properly set up before calling
.send(). This helps with debugging and provides valuable insights into any email issues.
Use
.send()as the Final CallThe
.send()method should be the last call in your chain. Ensure all other configurations (liketo(),from_address(), etc.) are set before calling.send().
Logging Customization
For more advanced logging, customize the format by providing a
custom_formatterfunction. This can help format error messages or traceback details according to your needs.The
custom_formatterfunction must accept two arguments: anExceptionand atracebackstring. This ensures security and prevents injection vulnerabilities.
Set Traceback Visibility During Development
Set the
show_traceback=Trueflag during development to view detailed error traces. This is helpful for debugging, but should be turned off in production to avoid exposing sensitive information.
Related folder names
Use the same
folder_namefor related templates (text and HTML):
This keeps your project organised, consistent, and easy to maintain.project/ โโโ templates/ โ โโโ email_templates/ โ โโโ authentication/ โ โโโ login/ โ โ โโโ login.html โ โ โโโ login.txt โ โโโ passwords/ โ โ โโโ password_reset.html โ โ โโโ password_reset.txt โ โโโ register/ โ โโโ register.html โ โโโ register.txt
Worst Practicesยถ
Skipping Logger Configuration
Avoid skipping logger configuration, especially in production. Not configuring a logger will result in missed error details, making it much harder to debug issues. Always pass a logger if you want insights into whatโs happening during the email sending process.
Calling
.send()Before Finalizing Email DetailsDonโt call
.send()before setting all required email details liketo(...),from_address(...),with_subject(...), etc. Calling.send()will result in an error raised.
Using a Custom Formatter with Incorrect Parameters
Donโt pass an invalid custom formatter**. The
custom_formatterfunction must accept exactly two arguments: anExceptionand atracebackstring. Passing in incorrect arguments will result in an error being raised.
Leaving Tracebacks Visible in Production
Never leave detailed tracebacks visible in production**. Tracebacks should only be shown in a development environment. Exposing them in production can reveal sensitive information to users or attackers, potentially causing security issues.
Ignoring Error Handling for Email Failures
Donโt ignore errors when email sending fails. Always ensure you have error handling in place, whether itโs logging or retries, so that youโre aware of any issues that occur during the email process.
Mixing Different Log Levels without Thought
Donโt randomly mix log levels without a clear purpose. Choose a log level for each message carefully (
INFO,WARNING,ERROR, etc.) to make your logs structured and easy to interpret.
Skipping the logger
Skipping the logger and then struggling to debug when an email fails.
Mixing unrelated templates together randomly:
Keeping related files in a different
folder_name
For example :
This makes your project messy, error-prone, and hard to maintain.
```md
project/
โโโ templates/
โ โโโ email_templates/
โ โโโ login.html
โ โโโ password_reset.txt
โ โโโ newsletter.html
โ โโโ random_folder/
โ โโโ register.txt
โ โโโ backup_templates/
โ โโโ login_backup.txt
โ โโโ reset_password_backup.html
โ โโโ marketing/
โ โโโ new_product.html
โ โโโ welcome.txt
```
Key issues:
Templates from different features (login, passwords, marketing) are thrown together with no clear separation.
Some templates are floating randomly without folders.
Backup files are mixed with production files.
Inconsistent folder naming (
random_folder,backup_templates, etc).Future developers (even you!) will struggle to find the correct template.
Higher chance of using the wrong template by mistake.
Licenseยถ
This package is licensed under the MIT License. See the LICENSE file for details.
Creditsยถ
-This library was created and maintained by Egbie Uku a.k.a EgbieAndersonUku1.