django-email-sender 2.0.6
A chainable Django email sender utility.
Requires Python: <3.14,>=3.10
Keywords: django, email, email-sender, html-email, plain-text-email, email-utility, rich-template, chaining, logger, logging, database-integration, email-templates, django-email

[![Downloads](https://static.pepy.tech/badge/django-email-sender)](https://pepy.tech/project/django-email-sender)
[![PyPI version](https://img.shields.io/pypi/v/django-email-sender/2.0.5.svg?color=blue)](https://pypi.org/project/django-email-sender/2.0.0/)

📚 **Documentation**: [View the full documentation here](https://django-email-sender.readthedocs.io/en/latest/)


# 📧 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+)  
- i18n support (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.


## Table of Contents

- [📧 Why Use This?](#why-use-this)
- [🔄 Upgrading from Version 1](#upgrading-from-version-1)
- [🆕 What's New in Version 2?](#what-is-new-in-version-2)
- [✨ Features](#features)
- [🔄 What is `EmailSender` and what is `EmailSenderLogger`, and why are they needed?](#what-is-emailsender-and-what-is-emailsenderlogger-and-why-are-they-needed)
- [🤔 Go to "What if I am not using the advanced features of `EmailSenderLogger`"](#what-if-i-am-not-using-the-advanced-features-of-emailsenderlogger)
- [📧 `EmailSender` Class API Reference](#emailsender-class-api-reference)
- [📝 `EmailSenderLogger` Class API Reference](#emailsenderlogger-class-api-reference)
- [🧼 Available Methods](#available-methods)
- [🔄 Additional API Notes](#additional-api-notes)
- [🧩`EmailSender` and `EmailSenderLogger` Methods](#emailsender-and-emailsenderlogger-methods)
- [🧼 Code Style Tips](#code-style-tips)
- [🚀 Installation via PyPI](#installation-via-pypi)
- [🧩 Requirements](#requirements)
- [📝 Logger and Database Integration](#logger-and-database-integration)
- [📝 How to configure the Logger](#how-to-configure-the-logger)
- [🔑 Enabling the Logger](#enabling-the-logger)
- [🛠️ Example Setting up a simple Logger with EmailSenderLogger](#example-setting-up-a-simple-logger-with-emailsenderlogger)
- [🔁 Reuse the Logger and Formatter Across Multiple Emails](#reuse-the-logger-and-formatter-across-multiple-emails)
- [🗣️ Turning on Verbose Mode](#turning-on-verbose-mode)
- [🧭 Tracing Method Chains and logging errors with `set_traceback`](#tracing-method-chains-and-logging-errors-with-set_traceback)
- [⚙️ Setting up an advanced logger](#setting-up-an-advanced-logger)
- [🔄 Advanced Tip: Rotate Log Files Automatically](#advanced-tip-rotate-log-files-automatically)
- [📈 Advanced Logger Usage](#advanced-logger-usage)
- [📝 Sample Logging Report](#sample-logging-report)
- [🗃️ Database Integration](#database-integration)
- [🛠️ Setting up an advanced logger](#setting-up-an-advanced-logger)
- [📝 Reuse the Logger and Formatter Across Multiple Emails](#reuse-the-logger-and-formatter-across-multiple-emails)
- [🔄 Resetting or Reusing the Instance Cleanly](#resetting-or-reusing-the-instance-cleanly)
- [📧 HTML Email Template Example](#html-email-template-example)
- [📝 Plain Text & Multi-part Email Support](#plain-text--multi-part-email-support)
- [🧱 Subclassing](#subclassing)
- [🛠️ Function-Based Abstractions](#function-based-abstractions)
- [📁 Templates](#templates)
- [📁 Configuring the Template Directory](#configuring-the-template-directory)
- [🌐 Multilingual Error Messages](#multilingual-error-messages)
- [🧩 Putting it all together Example](#putting-it-all-together)
- [🎮 Playing Around with Features Without Sending Emails](#playing-around-with-features-without-sending-emails)
- [🏆 Best Practices](#best-practices)
- [❌ Worst Practices](#best-practices)



## 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.

[🔝 Back to top](#table-of-contents)


## 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 `EmailLogger` integration.

- 🔄 **Auto-Reset After Sending**  
  Automatically reset all email fields after sending with `auto_reset=True` to prevent accidental resends.

- 📌 **Preserve Specific Fields**  
  Use `preserve_fields` to retain selected fields even after an auto-reset.

- 🧼 **Individual Field Clearing**  
  Clear only what you need with new methods like `clear_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.




[🔝 Back to top](#table-of-contents)

---


### Sample Code Comparison: Version 1 vs Version 2

**Version 1 (Classic Usage)**

```python

# 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
```python
## 

```python
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.


 ```python
 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 what is `EmailSenderLogger`, and why 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: `EmailSenderLogger` does **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`:

```python
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
```
---

[🔝 Back to top](#table-of-contents)

### 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`.

```python
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`.

---


[🔝 Back to top](#table-of-contents)


## Available Methods

Method Description

### EmailSender methods
```markdown

 - 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()
```

[🔝 Back to top](#table-of-contents)


### `EmailSender` Class API Reference

#### 🔨 `create()`
> **Factory method** — Instantiates and returns an `EmailSender` object.

#### 📤 `from_address(email)`
> **Sets the sender's email address**.  
> `email`: A string representing the sender's email (e.g. `noreply@yourdomain.com`).

#### 📥 `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
```python
    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 using `EmailSenderLogger`, 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** the `to_email` field 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**.  
> If `folder_name` is omitted, defaults to `emails_templates/`.

#### 🌐 `with_html_template(folder_name="folder-name-here", template_name="template-name-here.html")`
> **Specifies the HTML version of the email template**.  
> If `folder_name` is omitted, defaults to `emails_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

```python
    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.

```python
    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

```python
    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.


```python
    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.


```python
    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.


```python
    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.`

---

[🔝 Back to top](#table-of-contents)


###  `EmailSenderLogger` Class API Reference

### 🔨 `create()`
> **Factory method** — Instantiates and returns an `EmailSenderLogger` object.

---

#### 📤 `add_email_sender_instance(email_sender_instance)`
> Sets the core `EmailSender` instance 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 use `add_new_recipient` method 

---

#### 📥 `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.  
> If `folder_name` is omitted, defaults to `emails_templates/`.

---

#### 🌐 `with_html_template(folder_name, template_name)`
> Specifies the HTML version of the email template.  
> If `folder_name` is omitted, defaults to `emails_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. 


```python

  from django_email_sender.email_sender_constants import LoggerType

  email_logger.config_logger(my_logger, LoggerType.INFO)

```

 | Constant           | Description                              |
|--------------------|------------------------------------------|
| `LoggerType.INFO`  | Standard informational messages           |
| `LoggerType.WARNING` | Non-critical issues worth noting        |
| `LoggerType.ERROR` | Errors encountered during sending         |
| `LoggerType.DEBUG` | 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 the `abstract` base model `EmailBaseLog`
> The module path : `from django_email_sender.models import EmailBaseLog`

---

#### 🧪 `to_debug(message)`
> Logs a message at the `DEBUG` level.

---

#### ℹ️ `to_info(message)`
> Logs a message at the `INFO` level.

---

#### ⚠️ `to_warning(message)`
> Logs a message at the `WARNING` level.

---

#### ❌ `to_error(message)`
> Logs a message at the `ERROR` level.

---

#### 🔊 `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()`](#start_logging_session) 🔓  
> Starts the logging session and returns `self` for 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:

```plaintext
[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()`](#stop_logging_session) 🔒  
> Completely disables further logging and ends the session.

---

#### [`pause_logging()`](#pause_logging) ⏸️  
> Temporarily pauses logging without clearing state.

---

#### [`resume_logging()`](#resume_logging) ▶️  
> Resumes logging after a `pause_logging()` call.


#### 📬 `is_email_sent` (Property)
> Returns `True` if the last email was successfully sent, otherwise `False`.

---

#### 🔢 `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_changes` dictionary, 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 to `True`, default is `False`
> show_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 `subject` or `to`).  
> 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_fields` and `exclude_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.


[🔝 Back to top](#table-of-contents)


## 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](#emailsender-class-api-reference) 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                                        |
|----------------------------------------|----------------------|----------------------------------------------------|
| `EmailSenderConstants.Fields.FROM_EMAIL`        | `"from_email"`       | The sender's email address                         |
| `EmailSenderConstants.Fields.TO_EMAIL`          | `"to_email"`         | The recipient's email address                      |
| `EmailSenderConstants.Fields.SUBJECT`           | `"subject"`          | The subject line of the email                      |
| `EmailSenderConstants.Fields.HTML_TEMPLATE`     | `"html_template"`    | The HTML template used to render the email body    |
| `EmailSenderConstants.Fields.TEXT_TEMPLATE`     | `"text_template"`    | The plain text version of the email body           |
| `EmailSenderConstants.Fields.CONTEXT`           | `"context"`          | A dictionary of variables used in template rendering |
| `EmailSenderConstants.Fields.HEADERS`           | `"headers"`          | Optional custom email headers                      |
| `EmailSenderConstants.Fields.LIST_OF_RECIPIENT` | `"list_of_recipients"` | A list of recipient email addresses             |
| `EmailSenderConstants.Fields.EMAIL_ID`          | `"email_id"`         | Unique identifier for the email instance           |


📊 Logging-specific use (with EmailSenderLogger)

```python
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"


[🔝 Back to top](#table-of-contents)

###  `EmailSender` and `EmailSenderLogger` Methods

#### **Core Functions**

| Name                           | Type    | Chainable | Description                                      | Defined In           |
|---------------------------------|---------|-----------|--------------------------------------------------|----------------------|
| `create()`                     | Method  | ✅         | Factory method to instantiate the class          | Both                 |
| `