Cuando surge una nueva tecnología de comunicación que hace algo parecido a lo que hace el e-mail ordinario nunca falta nadie que diga la frase de "el e-mail ha muerto". Nada más lejos de la realidad.

Incluso con tecnologías y aplicaciones recientes como Twitter y Facebook incorporan el e-mail como manera de avisar a los usuarios de que algo está pasando en la red social. Recibimos ofertas, estamos subscritos a listas de correo, avisamos a los administradores de que las incidencias de las aplicaciones web por correo electrónico. Podríamos afirmar que el número de correos que enviamos y recibimos no ha disminuido sino que ha ido aumentando.

Django proporciona unas magníficas herramientas para el envío de correos electrónicos, recogidos como no en su excelente documentación, incluso nos proporciona distintos backends para que podamos probar nuestros envíos de correo. De este modo si nuestra apliación manda pocos tipos de e-mails las herramientas que nos proporciona Django de serie son más que suficientes para un uso habitual.

El problema surge cuando tenemos muchos tipos distintos de correos a enviar, imaginemos el caso de una red social, donde tenemos mails para el registro, recuperación de claves, confirmación de registro, cuando alguien nos menciona, avisos, estadística, ... Empezamos a tener muchos tipos de correo y en esos casos necesitaremos poderlos gestionar de manerea rápida y eficiente y no sólo eso normalmente querremos que el texto de los correos pueda cambiar rápido.

Es este tipo de situaciones las que intentaremos solventar en este artículo. Veremos qué herramientas y utilidades del ecosistema Django nos pueden ser útiles.

Supongo que siendo conocedores de Django a más de uno se le habrá ocurrido generar sus correos a partir de plantillas y utilizar la función render_to_string, si es así ya tenemos mucho ganado. Podemos crear un subdirectorio de plantillas de correo y nombrándolas adecuadamente tener controlados los distintos correos que queremos enviar y los datos que esperan cada una de ellas.:

from django.template.loader import render_to_string
from django.core.mail import EmailMessage
html_content = render_to_string('mail/registro.html', {'user': User})
from_email = 'no-reply@example.com'
to = 'someuser@example.com'
msg = EmailMessage(subject="Registro", html_content, from_email, [to])
msg.content_subtype ="html"
msg.send()

En este ejemplo vemos como utilizamos la plantilla registro.html que se encuentra en el directorio mail de alguna de las aplicaciones que tenemos registradas en Django.

Imaginemos ahora que el número de plantillas crece y que empezamos a tener código de envío de correos como este desperdigado por nuestra aplicación. En nada se volverá algo inmanejable.

Existen utilidades como Django Templated Email que intentan organizar esto en parte, dándonos unas reglas a la hora de generar las plantillas i una funciones de utilidad que nos permiten gestionar el envío de e-mails. Por ejemplo:

from templated_email import send_templated_mail
send_templated_mail(
        template_name='welcome',
        from_email='from@example.com',
        recipient_list=['to@example.com'],
        context={
            'username':request.user.username,
            'full_name':request.user.get_full_name(),
            'signup_date':request.user.date_joined
        },
        # Optional:
        # cc=['cc@example.com'],
        # bcc=['bcc@example.com'],
        # headers={'My-Custom-Header':'Custom Value'},
        # template_prefix="my_emails/",
        # template_suffix="email",
)

Es un avance pero todavía tenemos código que es difícil de leer. Si los remitentes son siempre los mismos o los parámetros que se cambian a las plantillas varían poco, podemos ir creando distintas funciones de utilidad, pero acaba resultando un código bastante farragoso.

De todos modos hemos dado un gran paso, ahora tratamos los correos no como texto sino como plantillas, de modo que estamos utilizando la potencia de las plantillas de Django para ir generando los distintos correos que necesitamos, y si mantenemos las plantillas organizadas será sencillo ir cambiandolas según se necesite. Tratamos el contenido de los correos como páginas web.

Per claro, en las últimas versiones de Django surgió la mejora de las Class Base Views (CBV), de las que ha hemos hablado en este blog. Las CBV nos permiter generar el HTML de las páginas Web (o más genéricamente el contenido) a partir de clase, con todo lo que comporta de herencia, mixins i reutilización de código.

Y supongo que esa es la potencia que buscaba la gente de Disqus cuando decidió crear las django-mailviews. Un conjunto de clases que nos permiten gestionra nuestros correos como si fuesen vistas de Django.

Ahora podemos utilizar la estructura de clases para poder organizar toda la información que necesitamos para poder generar un correo.

Supongamos que queremos enviar un e-mail personalizado a nuestros usuarios con las noticias de la semana, podríamos escribir una clase como:

class NoticiasSemanalesEmailView(TemplatedEmailMessageView):
   """
   Envio de las noticias semanalles
   """

   subject_template_name = 'mail/semanal/subject.html'
   body_template_name = 'mail/semanal/body_text.html'

   def __init__(self, user, *args, **kwargs):
       super(NoticiasSemanalesEmailView, self).__init__(*args, **kwargs)
       self.user = user

   def get_context_data(self, **kwargs):
       context = super(NoticiasSemanalesEmailView, self).get_context_data(**kwargs)
       context['user'] = self.user
       return context

   def render_to_message(self, *args, **kwargs):
       kwargs['to'] = (self.user.email, )
       return super(NoticiasSemanalesEmailView, self).render_to_message(*args, **kwargs)

De este modo enviar un mensaje pesonalizado a uno de nuestros usuarios es tan sencillo como

NoticiasSemanalesEmailView(user).send()

Ahora la gracia está en que si queremos hacer lo mismo con las notícias mensuales, simplemente tendremos que heredar de nuestra clase semanal, cambiando las plantillas que vamos a utilizar:

class NoticiasMensualesEmailView(NoticiasSemanalesEmailView):
    subject_template_name = 'mail/mensual/subject.html'
    body_template_name = 'mail/mensual/body_text.html

Para enviarle las noticias mensuales a nuestro usuario bastará un

NoticiasMensualesEmailView(user)send()

Con esto conseguimos por una parte reutilzar nuestro código, documentarlo y al mismo tiempo mantener nuestras plantillas de correo organizadas.

Fijémonos además que se utiliza el get_context_data del mismo modo que se utiliza en las CVB de Django, por lo que al final estamos utilizando un modo de programar consistente tanto para nuestras vistas como para nuestros correo.

El módulo mailviews nos proporciona dentro de messages un conjunto de clases de las que heredar para personalizar nuestros envíos de correo electrónico:

  • EmailMessageView Es la clase base que encapsula EmailMessage y de la que heredarán el resto de las clases implementando los métodos de renderización de contenidos.
  • TemplatedEmailMessageView Nos permite utilizar plantillas Django para crear tanto el asunto como el cuerpo del mensaje. El mensaje se envía como texto plano.
  • TemplatedHTMLEmailMessageView Clase idéntica a la anterior salvo que nos permite enviar el mensaje en formato HTML.

Si le damos un vistazo al código veremos que es realmente seguible. Al final se trata de una encapsulación de EmailMessage o bien de EmailMultiAlternatives, mailviews nos permite, previa configuración, poder visualizar los mensajes que vamos a enviar, lo que es extremadamente útil cuando estamos desarrollando.

Observemos también que el método que hace el trabajo de generar el mensaje es render_to_message y que devuelve una instancia de EmailMessage. Esto significa que si sobreescribimos bien este método tenemos a nuestra disposción una instancia de EmailMessage que podemos utilizar para, por ejemplo, adjuntar un archivo a nuestro correo.

Supongamos por ejemplo que el departamento de contabilidad deja las facturas que genera en un directorio llamado /tmp/facturas/ con el identificador del usuario, de modo que fra_1.pdf es la factura del usuario con identificador 1.

Podríamos crear una clase que se encargue de enviar cada factura a su usuario correspondiente, y además hacer que se envíe tanto en formato texto como en formato html. Para ello bastará heredar nuestra plantilla de TemplatedHTMLEmailMessageView y añadir una plantilla adicional que nos permita renderizar también el texto en formato html además del texto en formato plano.

Dado que se trata de una clase podríamor ir creando clases especializadas o Mixins para realizar las distintas acciones.

Las mailview proporcionan lo que se conoce como sintactic sugar alrededor de las clases de Django para enviar mail, haciendo que la generación de correos electrónicos nos descontrole y se convierta en una funcionalidad mantenible.