Factories

Create multiple similar emails

Imagine you want to inform a couple of users about something that happened in your system, for example a successful transaction or a report generated by your application. You would have a very similar mail body except for the salutation or one or two minor differences. Handling this with the single email class from above feels a little off.

That is why this package provides a mail factory! This factory is a wrapper for creating similar emails and providing the email class shown above with recipient-specific content.

Look at this example:

class MyFancyMail(BaseEmailService):
    subject = _('I am a great email!')
    template_name = 'email/my_fancy_email.html'


class MyFancyMailFactory(BaseEmailServiceFactory):
    service_class = MyFancyMail

    def __init__(self, action_id: int, recipient_email_list: list = None):
        super().__init__(recipient_email_list)
        self.action = Action.objects.filter(id=action_id).first()

    def get_recipient_list(self) -> list:
        return self.action.fetch_recipients_for_this_action()

    def get_email_from_recipient(self, recipient) -> str:
        if isinstance(recipient, User):
            return recipient.email
        return recipient

    def is_valid(self):
        if not self.action:
            raise EmailServiceConfigError('No action provided.')

        return super().is_valid()

    def get_context_data(self):
        context = super().get_context_data()
        context.update({
            'action': self.action,
        })
        return context

This is only one of many (!) possibilities to handle a case like described above. We pass a custom action id to the factory and fetch the given action (this might be a project, a report, a record…) and set it to a class attribute.

The get_recipient_list() method fetches the recipients based on the action we are looking at right now.

Because we might get mixed results (mostly not, but just to show what is possible), we overwrite the method get_email_from_recipient() to be able to handle simple email addresses as a string or user objects. If you pass only strings, overwriting this method can be omitted.

We added a sanity check in the is_valid() method to be sure that nobody tries to create emails like this without an action being set.

Finally, we add the action to the context data get_context_data() so the MyFancyMail() class can use it.

Now for every recipient an instance of MyFancyMail() will be created. Now it is no problem, handling the salutation or any other recipient-specific content within the “real” mail class. Only make sure that the factory provides all the required data.