# Class-based Emails

This package contains an approach called "class-based emails". Similar to class-based views in django (in comparison to
function-based ones), you can now implement your email as a class and use all the benefits of object-orientated
programming like fiddling around with functions and all the non-DRY repetition of code you'll usually end up with.

USPs:

* Better structure and advantages of object-orientated programming over handling multiple functions!
* Quick and fast creation of new emails — only worry about the things you have to think about!
* No code redundancy for setting up the default mail configuration — all wrapped in the class!
* The text part of the email can be automatically rendered from the HTML part — no redundant templates anymore!
* Built-in (and extendable) sanity checks as a validation for forgotten variables!
* Clean and easy solution for email attachments

There are two scenarios covered by this package:

* **Single mail**: Create a single email through a class to utilise benefits of object-orientation.
* **Similar mails**: Create a bunch of similar emails with a factory class, for example if you want to send the same
  content but with a personal salutation to a number of people.

## Create a single email

Imagine you want to send a single email to a given user or to the system admins. Instead of having to deal with how to
end an email and worry about if the ``to`` field requires a list or string of emails... look at this example:

````python
from django_pony_express.services.base import BaseEmailService

class MyFancyClassBasedMail(BaseEmailService):
    """
    Send an email to my admins to inform them about something important.
    """
    subject = _('Heads up, admins!')
    template_name = 'email/my_fancy_class_based_email.html'

    def get_context_data(self):
        data = super().get_context_data()
        data['my_variable'] = ...
        return data
````

This is a simple example of how to create an email. We pass or set the recipients in the `__init__()` method and can add
more data in the `get_context_data()` - or just provide the context on creation as a parameter.

One big advantage is that you can create your own base class which handles all the context data you need to have for
your base email template. Imagine you have an unsubscribe link or a logo in the base template. In the "old world"
you have to think to pass these variables every time. Now, just wrap it up in a base class and that's it!

And that is how you would send the email:

````python
from django.conf import settings

email_service = MyFancyClassBasedMail(settings.MY_ADMIN_EMAIL_ADDRESS)
email_service.process()
````

Optionally you can set the class attribute ``template_txt_name`` to define a plain text template. If not set, the HTML
part will be used to render the plain text body.

## Attachments

If you want to attach a number of files to your emails, you can do this in two ways.

The simple way is passing an absolute file path to the constructor of the service:

````python
email_service = MyMailService(
  ...
  attachment_list=[my_file_1, my_file_2]
)
````

If you want to customise the filename or even pass a mimetype, you can do as follows:

````python
email_service = MyMailService(
  ...
  attachment_list=[{'filename': 'my_fancy_file.json', 'file': file_content, 'mimetype': 'application/json'}]
)
````

Please note that here the file content, not the file path, needs to be passed to the attachment list. If anything goes
sideways, the service will throw an `EmailServiceAttachmentError` exception.

## Async dispatching

A general rule about external APIs is that you shouldn't talk to them in your main thread. You don't have any control
over it, and it might be blocking your application. Therefore, it's wise to use some kind of asynchronous method to send
your emails.

The base class in this package can be used in an async way very simply. Call the process method of your email
inheriting from `BaseEmailService` in, for example, a thread or celery task.

### Python Threads

If you don't want to do this, you can use the `ThreadEmailService` class, which will wrap the sending of your emails in
a simple Python thread.

````python
from django_pony_express.services.asynchronous.thread import ThreadEmailService


class MyThreadBasedEmail(ThreadEmailService):
    pass
````

### Other methods

In the future, we'll add a base class for Celery and maybe django-q / django-q2.
