El Blog de Trespams

Blog personal sobre tecnologia, gestió de projectes i coses que se me passen pel cap

Mails amb Django - III

Fins ara hem vist com podem enviar correus electrònics amb format text, amb format HTML en el dos formats. També hem vist que podem generar el nostres correus a partir de plantilles Django.

A poc que l'aplicació es vaig fent més gran veurem que és molt interessant poder tenir tots els correus que enviam centralitzats, de manera que no hagem d'anar a cercar per codi i per les diferents aplicacions els correus que enviam.

Sovint a més els correus que enviam són semblants: van dirigits a la mateixa gent, o tenen informació que canvia molt poc d'uns als altres, com el peu del missatges o la salutació.

Una situació ideal per utilitzar dues coses: les plantilles de Django per a la generació de correus i aprofitar com Django va a cercar les plantilles per tenir tots els correus centralitzats a un mateix lloc, i per una altra banda l'herència pròpia de Python que ens permetrà guanyar temps a l'hora de generar els correus i farà que siguem menys propensos a error a l'hora de generar-los.

Ara és quan a un se li encén la bombeta i es posa com a un boig a generar classes de Python per a la generació de correus amb Django. No faceu tanta via! La primera regla del bricolatge: "si està fet compra-ho". Així que el primer que hem de fer és cercar si ja hi ha alguna cosa feta. I com no, hi és. De fet hi ha vàries llibreries per a la generació de correus fent servir plantilles Django. Una de les més interessants és la que ha fet Ted Kaemming anomenada django-mailviews que a més d'encapsular tot això en classes Python, ho ha posat dins un paquet Django que ens facilita la vida de visualitzar els correus.

La documentació no és tot el completa/acurada que hauria de ser i és focalitza més en la visualització que en el que jo trob més interessant que és aquesta possibilitat d'encapsulació. Així que fork al canto per a millorar la documentació!

En aquest article faré servir aquesta llibreria per mostrar com fer les tasques més habituals i que ja hem vist abans. Ja que hi som aprofitaré també per mostrar com podem enviar un fitxer adjunt.

Enviar un correu sols de text

Volem enviar un correu a un usuari.

Definirem les dues plantilles que utilitzarem, la primera per l'assumpte (subject) y l'altra per al contingut.

Cream un directori anomenat mail (sóc així d'original) dins templates de la nostra aplicació principal. A partir d'aquí podríem anar creant diferents subdirectoris segons els correus per a enviar, però supòs que ja es veu la idea...

La plantilla per l'assumpte queda com:

{# plantilla 'mail/subject.html' Greetings {{user.get_full_name}}

i seguidament definim el cos del missatge

{# plantilla 'mail/body_text.html' #} Dear {{user.get_full_name}}, You are the best. Please give us your money!

El codi per enviar aquest missatge podria ser alguna cosa semblant a:

from mailviews.messages import TemplatedEmailMessageView
from django.contrib.auth.models import User


class SimpleSpamView(TemplatedEmailMessageView):
""" Classe per spamejar l'usuari demo i dir-li que ens doni els doblers """
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'

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

def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email,)
return super(SimpleSpamView, self).render_to_message(*args, **kwargs)

i per enviar-lo bastaria cridar:

SimpleSpamView().send()

Pareix molta feina comparat amb el que teníem, veritat? Doncs sí, però com passa amb els class based views de Django aquesta feina extra inicial es veu compensada de sobres per la claretat del codi, que d'aquesta manera queda molt documentat, separant el text de generació, i sobretot possibilitant la reutilització.

Per cert la sortida d'aquest correu és:

Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: Greetings Demy Ostracion From: webmaster@localhost To: demo@example.com Date: Thu, 28 Mar 2013 11:40:53 -0000 Message-ID: <20130328114053.6625.94194@T1600> Dear Demy Ostracion, You are the best. Please give us your money!

Com podem veure es tracta d'un missatge codificat com a text, que és precisament el que cercàvem.

Correu amb text i HTML

Sabem que Demy, el nostre usuari de proves, fa servir un lector de correu que permet el format HTML, així que per reforçar més el missatge el que farem serà enfatitzar el missatge generant una plantilla HTML

{# mail/body_html.html #} Dear {{user.get_fullname}}, <h1>You are the best!</h1> <strong>Please give us your money!</strong>

Ara el que farem és que enlloc de fer herència de TemplatedEmailMessageView la farem de la classe TemplatedHTMLEmailMessageView que té un atribut nou html_body_template_name

from mailviews.messages import TemplatedHTMLEmailMessageView
from django.contrib.auth.models import User

class SimpleSpamView(TemplatedHTMLEmailMessageView):
""" Classe per spamejar l'usuari demo i dir-li que ens doni els doblers """
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'
html_body_template_name = 'mail/body_html.html'

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

def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email,)
return super(SimpleSpamView, self).render_to_message(*args, **kwargs)

i l'enviam com abans

SimpleSpamView().send()

que té per sortida:

Content-Type: multipart/alternative; boundary="===============6944209950729072063==" MIME-Version: 1.0 Subject: Greetings Demy Ostracion From: webmaster@localhost To: demo@example.com Date: Thu, 28 Mar 2013 11:56:34 -0000 Message-ID: <20130328115634.6625.91235@T1600> --===============6944209950729072063== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Dear Demy Ostracion, You are the best. Please give us your money! --===============6944209950729072063== Content-Type: text/html; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Dear Demy Ostracion, <h1>You are the best!</h1> <strong>Please give us your money!</strong>

Adjuntant arxius a un correu

Des de marketing ens diuen que ara el que convé és adjuntar fitxers en pdf per reforçar encara més el missatge i tenir el nostre usuari entretingut, així que ens passen un pdf amb informació corporativa que hem d'adjuntar a cada e-mail que li enviam a Demy.

Nosaltres el desarem al nostre directori pdf, però tenim un problema, la classe que estam fent servir no diu res d'adjuntar pdf. Però nins, això és codi obert i li podem fer una ullada al codi. Com podem veure el que fa render_to_message és tornar-nos una instància de la classe EmailMessage de Django i aqueta té dos mètodes per adjuntar imatges attach i attach_file. La primera la farem servir principalment quan tenim un fluxe de dades (per exemple hem generat nosaltres el pdf i està en memòria) i la segona quan tenim el fitxer dins el sistema d'arxius.

from mailviews.messages import TemplatedHTMLEmailMessageView
from django.contrib.auth.models import User


class SimpleSpamView(TemplatedHTMLEmailMessageView):
""" Classe per spamejar l'usuari demo i dir-li que ens doni els doblers """
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'
html_body_template_name = 'mail/body_html.html'

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

def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email,)
msg = super(SimpleSpamView, self).render_to_message(*args, **kwargs)
msg.attach_file('./pdfs/test.pdf')
return msg


SimpleSpamView().send()

Que torna:

Content-Type: multipart/mixed; boundary="===============5276732835281812163==" MIME-Version: 1.0 Subject: Greetings Demy Ostracion From: webmaster@localhost To: demo@example.com Date: Thu, 28 Mar 2013 12:11:51 -0000 Message-ID: <20130328121151.6625.85753@T1600> --===============5276732835281812163== Content-Type: multipart/alternative; boundary="===============7874338344503940985==" MIME-Version: 1.0 --===============7874338344503940985== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Dear Demy Ostracion, You are the best. Please give us your money! --===============7874338344503940985== Content-Type: text/html; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Dear Demy Ostracion, <h1>You are the best!</h1> <strong>Please give us your money!</strong> --===============7874338344503940985==-- --===============5276732835281812163== Content-Type: application/pdf MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="test.pdf" JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURl Y29kZT4+CnN0cmVhbQp4nDPQM1Qo5ypUMFAwALJMLU31jBQsTAz1LBSKUrnCtRTyIHJAWJTO5RTC ZWoGlDI3NwEqDklR0HczVDA0UghJi7YxMLQzs7AxMLIztDEwttM1sjEwsYsN8eJyDeEK5ApUAAB7 .... .... --===============5276732835281812163==--

Com podeu veure s'ha enviat tant el text pla, l'html i el nostre adjunt.

En una aplicació real el path cap a l'arxiu seria una variable i/o estaria codificat de manera absoluta, però per l'exemple ja em perdonareu.

A partir d'aquí ja podeu veure que les possibilitats de personalització són infinites. Pensau que en una aplicació real amb molts correus a enviar, poder organitzar el codi en classes ens permetrà reaprofitar molta feina, i sobretot ens llevarà feina de depuració a l'hora d'anar canviant el texte del correu o les condicions en que s'ha d'enviar un correu.

I al proper article parlarem d'enviaments, coes, registre i depuració. Tot en un mateix paquet django-mailer2.

blog comments powered by Disqus