[ x ]

Faig servir les cookies de Google Analytics per al control de visites i estadístiques..
És una pardalada, però la llei diu que us he d'avisar, ja veus. Així que si visitau aquest blog donau-vos per informats o sortiu ara mateix i netejau les cookies del vostre navegador. Si continuau llegint, suposaré que ja us està bé. Si vols saber com llevar les cookies del teu navegador: aquí ho pots trobar

Mails amb Django - I

En aquest article, o millor dit, sèrie d'articles, intentaré explicar el millor que pugui la problemàtica que ens trobam en el dia a dia la gent que hem d'enviar correus electrònics des de les nostres aplicacions Django, no tant des del punt de vista de com es fa, sinó del programador que té que desenvolupar d'aplicació.

Veurem distints escenaris i com podem tenir una solució que s'adapta a cada un d'ells, de manera que acabem amb un petit receptari, que esper anar ampliant amb l'adjuda de tots. La referència fonamental per Django és la documentació "sending email", miraré de no repetir-la molt, però per completitud convé fer una petita introducció.

Configurar el servidor

Volem enviar un e-mail ràpid en resposta a un esdeveniment.

Per a poder enviar un e-mail des de la nostra aplicació Django prèviament haurem d'haver configurat en nostre servidor SMTP. Això es fa amb les variables del settings EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER i EMAIL_HOST_PASSWORD. Ja que hi som convé definir el mail que farem servir en cas que no el posem de manera explícita al DEFAULT_FROM_EMAIL

Normalment no hem de definir res si el nostre servidor d'e-mail és el mateix servidor on està l'aplicació i tenim un SMPT local, però pot ser necessari si volem fer servir un altre servidor amb un compte específic, com per exemple el gmail de Google podríem fer:

DEFAULT_FROM_EMAIL='jo@exemple.com'                                     
EMAIL_USE_TLS = True                                                           
EMAIL_HOST = 'smtp.gmail.com'                                                  
EMAIL_HOST_USER = 'usuari-gmail@gmail.com'                                      
EMAIL_HOST_PASSWORD = 'clau-super-secreta'                                         
EMAIL_PORT = 587

Enviar un e-mail

Volem enviar un mail a un conjunt de destinataris, per això podem fer servir la funció send_mail que ens proporciona Django

from django.core.mail import send_mail
send_mail(subject='test email 2', message='hello world 2', 
    from_email='antoni.aloy@example.com', 
    recipient_list=['un@apsl.net', 'dos@trespams.com'], fail_silently=False)

Aquí convé notar que hem de posar tant qui envia el mail from_email com la llista de destinataris. És un error molt comú posar al recipient_list el mail de la persona que ha de rebre l'e-mail sense aturar-se a pensar que la funció necesita una llista com a paràmetre. Si sols teniu una adreça que ha de rebre l'e-mail pensau que l'hem de convertir primer en una llista

from django.core.mail import send_mail
send_mail(subject='test email 2', message='hello world 2', 
    from_email='antoni.aloy@example.com', 
    recipient_list=['un@apsl.net',], fail_silently=False)

Si volem fer enviaments massius Django a les darreres versions incorpora la funció send_mass_mail que permet enviar correus sense tenir que obrir cada vegada la connexió amb el servidor.

Django a més ens proporciona dues funcions adicionals per simplificar el codi necessari per enviar els correus quan els destinataris són la llista d'administradors del lloc i el managers dels mateix, definits respectivament a les configuracions ADMINS i MANAGERS, que són mail_admins i mail_managers.

D'aquesta manera, com ja sabem quins són els destinataris sols hem d'omplir l'assumbte (subject) i el missatge (message) i ja ho tenim. Aquestes funcions agafen un paràmetre adicional html_message que ens permet enviar el missatge com a text/html enlloc del text/plain definit per defecte.

Així per exemple:

from django.core.mail import mail_managers
mail_managers(u'Avís nou usuari', u"Tens un nou usuari al sistema")

Ens serviria per enviar un correu quan es crea un nou compte d'usuari amb ben poca feina.

Enviar un correu en format HTML

Encara que la manera més ràpida i simple d'enviar un correu sia el text pla, sovint ens trobam que els nostres clients volen que el coreu s'envïi en format html, amb tota mena de decoració: negreta, cursives, links, imatges, arxius adjunts, ...

Això representa una nova complexitat, no per la dificultat d'enviar-ho, que com veurem és prou senzilla, sinó perquè no hi ha cap garantia que es vegi com un vol a la majoria de clients de correu. Per això el que més convé és fer els HTML dels correus el més senzill possibles, sense floritures i tornant a la programació a l'antiga. La gent de mailchimp té un bon grapat de consells damunt el tema i fins i tot va alliberar en el seu dia un bon conjunt de plantilles que ens poden servir de guia i que trobareu a github baix el nom de email-blueprints.

Començarem pel més senzill: suposarem que sols volem enviar un correu en format HTML sense part en text pla.

from django.core.mail import EmailMessage
msg = EmailMessage(subject="hello", 
    body="""<strong>Hello</strong> World""",
    from_email="jo@exemple.com",
    to=["someotheruser@example.com", ])
msg.content_subtype = "html"
msg.send()

La classe EmailMessage ens permet a més definir una llista de destinataris ocults (el típic bbc), destinataris amb còpia, afegir arxius, modificar les capçaleres o definir com establirem la connexió amb el sistema d'enviament de missatges. Hi tornarem un poc més tard amb això.

En missatges realment importants, on volem que es faci la lectura independentment del client de correu, convé enviar tant la part HTML com la part tn text plà. Django també ens permet fer-ho amb facilitat fent servir la classe EmailMultiAlternatives. Com a sublcasse d'EmailMessage ens permet fer el mateix que aquesta, però a més ens permet definir tant la part de text plà com l'html. D'aquesta manera el nostre *hello world" quedaria

from django.core.mail.import EmailMultiAlternatives
text_content = "hello wolrd!"
html_content = "hello <strong>world!</strong>"
msg = EmailMultiAlternatives(subject, 
    text_content=text_content, 
    from_email="jo@example.com", 
    to=["someone@example.com"])
msg.attach_alternative(html_content, "text/html")
msg.send()

Testejar els mails

Anam avançant en el tema, ara ja sabem com enviar mails en text pla i en format HTML, enviar-los als administradors, a una persona, a vàries, ... Tot això està molt bé, però com que fem les proves amb la nostra pròpia compta de correu aquesta comença a estar plena de mails de proves i hem d'anar amunt i avall amb el client de correu.

Com bé podeu suposar hi ha d'haver una manera de poder fer feina amb els correus sense aquests inconvenients. La resposta està en els backends que Django ens permet definir a la configuració.

Per defecte Django configura la variable EMAIL_BACKEND com

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

això vol dir que per defecte Django intentarà enviar l'e-mail mitjançant el servidor smpt que hem definit dins EMAIL_HOST (que és localhost per defecte), si no tenim el servidor smtp configurat ens donarà error a l'hora d'enviar el correu.

Podem substituir aquest comportament per defecte per un backend alternatiu, a l'hora de testejar i veure que està passant, sobretot si estam en el servidor de desenvolupament de Django, podem fer que el missatge s'enviï per la consola canviant el backend a

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

així, encara que la configuració que tinguem sigui bonan, podem fer que el missatge no s'enviï i que sols surti per la consola.

Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: hello
From: jo@example.com
To: unaltre@exemple.net
Date: Sun, 24 Mar 2013 19:23:41 -0000
Message-ID: <20130324192341.9841.81239@T1600>

<strong>Hello</strong> World
-------------------------------------------------------------------------------

pràctic, veritat?

També hi ha un altre backend que pot ser útil, el File backend que enlloc de mostrar el missatge a la consola ens permet definir un directori on deixar-hi els correus mitjançant la variable EMAIL_FILE_PATH, però la veritat és que no l'he fet servir gens, com veurem hi ha altres alternatives més potents i que també ens faciliten la vida a l'hora de desenvolupar.

En els propers apunts ...

Fins ara no hem vist res que no sigui a la documentació de Django, però esper que us hagi servit d'introducció ràpida per al que vindrà després:

  • Generar correus amb les plantilles Django
  • Alternatives per generar els correus
  • Backends alternatius: django-mailer2
Comentaris
  1. Edu Herraiz Edu Herraiz on 25/03/2013 11:01 #

    Bona entrada Toni.

    Trob a faltar comentar una app que es diu django-celery-email (https://pypi.python.org/pypi/django-celery-email) que emplea el celery, i crea una tasca que envía el correu. Així, per enviar correus desde el frontend no has de fer esperar al usuari a que envii el email. La conexió smtp sol ser bastant lenta.

    Quedaría com un altre backend apart per triar:

    EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend'

    Salut
    Edu

  2. aaloy aaloy on 25/03/2013 11:11 #

    Edu, el django-celery-email introdueix un nivell de complexitat més, que és el celery pera a la gestió de coes i que sols per enviar mails no és convenient.

    El django-mailer2 té el mateix efecte, és a dir, evitar el temps d'espera per mor del temps de resposta del servidor smtp remot, però la configuració sols depén de una simple configuració de cron.

    Si tot va bé hi arribarem a tot això :D

  3. David Arcos David Arcos on 25/03/2013 12:34 #

    Molt interessant el Email-Blueprints, li farem una ullada :)

Els pingbacks estan tancats.