Crear plantillas de correo electrónico con Django

207

Quiero enviar correos electrónicos HTML, usando plantillas de Django como esta:

<html>
<body>
hello <strong>{{username}}</strong>
your account activated.
<img src="mysite.com/logo.gif" />
</body>

No encuentro nada send_maily django-mailer solo envía plantillas HTML, sin datos dinámicos.

¿Cómo uso el motor de plantillas de Django para generar correos electrónicos?

Anakin
fuente
3
Observe las 1.7ofertas de Django html_messageen send_email stackoverflow.com/a/28476681/953553
andilabs
Hola @anakin, he luchado con este problema durante mucho tiempo y decidí crear un paquete para eso. Me gustaría recibir sus comentarios: github.com/charlesthk/django-simple-mail
Charlesthk

Respuestas:

385

Desde los documentos , para enviar un correo electrónico HTML, desea utilizar tipos de contenido alternativos, como este:

from django.core.mail import EmailMultiAlternatives

subject, from_email, to = 'hello', '[email protected]', '[email protected]'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()

Probablemente desee dos plantillas para su correo electrónico: una de texto sin formato que se vea así, almacenada en su directorio de plantillas en email.txt:

Hello {{ username }} - your account is activated.

y uno HTMLy, almacenado en email.html:

Hello <strong>{{ username }}</strong> - your account is activated.

Luego, puede enviar un correo electrónico utilizando ambas plantillas haciendo uso de get_templateesta manera:

from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
from django.template import Context

plaintext = get_template('email.txt')
htmly     = get_template('email.html')

d = Context({ 'username': username })

subject, from_email, to = 'hello', '[email protected]', '[email protected]'
text_content = plaintext.render(d)
html_content = htmly.render(d)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
Dominic Rodger
fuente
40
Creo que puede simplificar esto con render_to_string , lo que le permitiría perder las líneas separadas que asignan plantillas a plaintexty htmly, y simplemente establecer plantillas y contextos cuando define text_contenty html_content.
cms_mgr
@cms_mgr ¿Puede elaborar lo que quiere decir y cómo podemos usarlo
akki
3
@akki vea la respuesta de andi a continuación, que también simplifica la parte alternativa gracias a que se agregó html_message param a send_email () en Django 1.7
Mike S
Disculpe, pero ¿por qué usamos txt y htmly ambos al mismo tiempo por correo? No entendí esta lógica
Shashank Vivek
son solo ejemplos para mostrar diferentes tipos de métodos, puede usar cualquiera de ellos @ShashankVivek
erdemlal
242

¡Niños y niñas!

Desde la versión 1.7 de Django en el método send_email , html_messagese agregó el parámetro.

html_message: si se proporciona html_message, el correo electrónico resultante será un correo electrónico multiparte / alternativo con mensaje como tipo de contenido de texto / sin formato y html_message como tipo de contenido de texto / html.

Entonces puedes simplemente:

from django.core.mail import send_mail
from django.template.loader import render_to_string


msg_plain = render_to_string('templates/email.txt', {'some_params': some_params})
msg_html = render_to_string('templates/email.html', {'some_params': some_params})

send_mail(
    'email title',
    msg_plain,
    '[email protected]',
    ['[email protected]'],
    html_message=msg_html,
)
andilabs
fuente
1
Tenga en cuenta si 'email.txt' y 'email.html' se encuentran en las plantillas de directorio definidas en la configuración que simplemente render_to_string ('email.txt', {'some_params': some_params} _
Bruno Vermeulen
Gracias por la render_to_stringpista, muy útil.
hoefling
1
¡Buena solución! Sin embargo, con send_mailno es posible establecer un encabezado personalizado como, por ejemplo, Return-Pathque se puede configurar con elEmailMultiAlternatives's constructor header parameter
Qlimax
26

He creado django-templated-email en un esfuerzo por resolver este problema, inspirado en esta solución (y la necesidad de, en algún momento, cambiar de usar plantillas de django a usar un conjunto de plantillas de mailchimp, etc. para correos electrónicos transaccionales con plantilla para mi propio proyecto) Sin embargo, todavía es un trabajo en progreso, pero para el ejemplo anterior, haría lo siguiente:

from templated_email import send_templated_mail
send_templated_mail(
        'email',
        '[email protected]',
        ['[email protected]'],
        { 'username':username }
    )

Con la adición de lo siguiente a settings.py (para completar el ejemplo):

TEMPLATED_EMAIL_DJANGO_SUBJECTS = {'email':'hello',}

Esto buscará automáticamente plantillas denominadas 'templated_email / email.txt' y 'templated_email / email.html' para las partes simple y html respectivamente, en los directorios / cargadores de plantillas normales de django (quejándose si no puede encontrar al menos uno de esos) .

Darb
fuente
1
Me parece bien. He recortado esto y lo he tirado en un ticket para agregar django.shortcuts.send_templated_mail: code.djangoproject.com/ticket/17193
Tom Christie
Genial, me alegra ver que se propone como una herramienta para django core. Mi caso de uso / enfoque para la biblioteca es un poco más grande que solo el acceso directo (cambio fácil entre proveedores de correo que tienen api de clave / valor para el envío de correo), pero parece una característica faltante del núcleo
Darb
15

Use EmailMultiAlternatives y render_to_string para utilizar dos plantillas alternativas (una en texto plano y otra en html):

from django.core.mail import EmailMultiAlternatives
from django.template import Context
from django.template.loader import render_to_string

c = Context({'username': username})    
text_content = render_to_string('mail/email.txt', c)
html_content = render_to_string('mail/email.html', c)

email = EmailMultiAlternatives('Subject', text_content)
email.attach_alternative(html_content, "text/html")
email.to = ['[email protected]']
email.send()
Rick Westera
fuente
5

He creado Django Simple Mail tener una forma sencilla, personalizable y plantilla reutilizable para cada correo electrónico transaccional que desea enviar.

El contenido y las plantillas de los correos electrónicos se pueden editar directamente desde el administrador de django.

Con su ejemplo, registraría su correo electrónico:

from simple_mail.mailer import BaseSimpleMail, simple_mailer


class WelcomeMail(BaseSimpleMail):
    email_key = 'welcome'

    def set_context(self, user_id, welcome_link):
        user = User.objects.get(id=user_id)
        return {
            'user': user,
            'welcome_link': welcome_link
        }


simple_mailer.register(WelcomeMail)

Y envíalo de esta manera:

welcome_mail = WelcomeMail()
welcome_mail.set_context(user_id, welcome_link)
welcome_mail.send(to, from_email=None, bcc=[], connection=None, attachments=[],
                   headers={}, cc=[], reply_to=[], fail_silently=False)

Me encantaría recibir cualquier comentario.

Charlesthk
fuente
Sería de gran ayuda si carga una aplicación de demostración de su paquete en su repositorio.
ans2human
Hola @ ans2human, gracias por esta sugerencia, ¡la agrego a la lista de mejoras!
Charlesthk
3

Hay un error en el ejemplo ... si lo usa como está escrito, se produce el siguiente error:

<tipo 'excepciones.Excepción'>: el objeto 'dict' no tiene el atributo 'render_context'

Deberá agregar la siguiente importación:

from django.template import Context

y cambia el diccionario para que sea:

d = Context({ 'username': username })

Ver http://docs.djangoproject.com/en/1.2/ref/templates/api/#rendering-a-context

idbill
fuente
Gracias, esto ya está arreglado.
Dominic Rodger
3

Django Mail Templated es una aplicación Django rica en funciones para enviar correos electrónicos con el sistema de plantillas Django.

Instalación:

pip install django-mail-templated

Configuración:

INSTALLED_APPS = (
    ...
    'mail_templated'
)

Modelo:

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block body %}
{{ user.name }}, this is the plain text part.
{% endblock %}

Pitón:

from mail_templated import send_mail
send_mail('email/hello.tpl', {'user': user}, from_email, [user.email])

Más información: https://github.com/artemrizhov/django-mail-templated

corredor
fuente
Esto fue realmente fácil de usar. Gracias.
cheenbabes
Hola, ¿cómo puedo configurar todos mis destinatarios en BCC?
aldesabido
@aldesabido Esto es solo un contenedor alrededor de la clase estándar EmailMessage de Django. Por lo tanto, debe leer la documentación oficial cuando busque estas características: docs.djangoproject.com/en/1.10/topics/email También eche un vistazo a una pregunta similar: stackoverflow.com/questions/3470172/…
raacer
Para ser más precisos, el EmailMessage estándar no se ajusta, sino que se hereda. Es decir, esta es una extensión para la clase estándar :)
raacer
¿Es posible incluir JS / CSS en la plantilla?
Daniel Shatz
3

Sé que esta es una pregunta antigua, pero también sé que algunas personas son como yo y siempre están buscando respuestas actualizadas , ya que las respuestas antiguas a veces pueden tener información obsoleta si no se actualizan.

Ahora es enero de 2020, y estoy usando Django 2.2.6 y Python 3.7

Nota: utilizo DJANGO REST FRAMEWORK , el código a continuación para enviar correos electrónicos estaba en una vista de modelo en miviews.py

Entonces, después de leer varias respuestas agradables, esto es lo que hice.

from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives

def send_receipt_to_email(self, request):

    emailSubject = "Subject"
    emailOfSender = "[email protected]"
    emailOfRecipient = '[email protected]'

    context = ({"name": "Gilbert"}) #Note I used a normal tuple instead of  Context({"username": "Gilbert"}) because Context is deprecated. When I used Context, I got an error > TypeError: context must be a dict rather than Context

    text_content = render_to_string('receipt_email.txt', context, request=request)
    html_content = render_to_string('receipt_email.html', context, request=request)

    try:
        #I used EmailMultiAlternatives because I wanted to send both text and html
        emailMessage = EmailMultiAlternatives(subject=emailSubject, body=text_content, from_email=emailOfSender, to=[emailOfRecipient,], reply_to=[emailOfSender,])
        emailMessage.attach_alternative(html_content, "text/html")
        emailMessage.send(fail_silently=False)

    except SMTPException as e:
        print('There was an error sending an email: ', e) 
        error = {'message': ",".join(e.args) if len(e.args) > 0 else 'Unknown Error'}
        raise serializers.ValidationError(error)

¡Importante! Entonces, ¿cómo render_to_stringllega receipt_email.txty receipt_email.html? En mi settings.py, tengo TEMPLATESy debajo es cómo se ve

Presta atención a DIRSesta línea os.path.join(BASE_DIR, 'templates', 'email_templates') . Esta línea es lo que hace que mis plantillas sean accesibles. En mi project_dir, tengo una carpeta llamada templates, y un subdirectorio llamado email_templatesasí project_dir->templates->email_templates. Mis plantillas receipt_email.txty receipt_email.htmlestán debajo del email_templatessubdirectorio.

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'templates', 'email_templates')],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
    },
},
]

Permítanme agregar eso, mi recept_email.txtaspecto es así;

Dear {{name}},
Here is the text version of the email from template

Y mi receipt_email.htmlaspecto es así;

Dear {{name}},
<h1>Now here is the html version of the email from the template</h1>
manpikin
fuente
0

Escribí un fragmento que le permite enviar correos electrónicos procesados ​​con plantillas almacenadas en la base de datos. Un ejemplo:

EmailTemplate.send('expense_notification_to_admin', {
    # context object that email template will be rendered with
    'expense': expense_request,
})
Andrii Zarubin
fuente
0

Si desea plantillas de correo electrónico dinámicas para su correo, guarde el contenido del correo electrónico en las tablas de su base de datos. Esto es lo que guardé como código HTML en la base de datos =

<p>Hello.. {{ first_name }} {{ last_name }}.  <br> This is an <strong>important</strong> {{ message }}
<br> <b> By Admin.</b>

 <p style='color:red'> Good Day </p>

En sus puntos de vista:

from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template

def dynamic_email(request):
    application_obj = AppDetails.objects.get(id=1)
    subject = 'First Interview Call'
    email = request.user.email
    to_email = application_obj.email
    message = application_obj.message

    text_content = 'This is an important message.'
    d = {'first_name': application_obj.first_name,'message':message}
    htmly = FirstInterviewCall.objects.get(id=1).html_content #this is what i have saved previously in database which i have to send as Email template as mentioned above HTML code

    open("partner/templates/first_interview.html", "w").close() # this is the path of my file partner is the app, Here i am clearing the file content. If file not found it will create one on given path.
    text_file = open("partner/templates/first_interview.html", "w") # opening my file
    text_file.write(htmly) #putting HTML content in file which i saved in DB
    text_file.close() #file close

    htmly = get_template('first_interview.html')
    html_content = htmly.render(d)  
    msg = EmailMultiAlternatives(subject, text_content, email, [to_email])
    msg.attach_alternative(html_content, "text/html")
    msg.send()

Esto enviará a la plantilla HTML dinámica lo que ha guardado en Db.

Javed
fuente
0

send_emai()no funcionó para mí, así que lo usé EmailMessage aquí en django docs .

He incluido dos versiones de la respuesta:

  1. Solo con la versión de correo electrónico html
  2. Con versiones de correo electrónico de texto sin formato y correo electrónico html
from django.template.loader import render_to_string 
from django.core.mail import EmailMessage

# import file with html content
html_version = 'path/to/html_version.html'

html_message = render_to_string(html_version, { 'context': context, })

message = EmailMessage(subject, html_message, from_email, [to_email])
message.content_subtype = 'html' # this is required because there is no plain text email version
message.send()

Si desea incluir una versión de texto sin formato de su correo electrónico, modifique lo anterior de la siguiente manera:

from django.template.loader import render_to_string 
from django.core.mail import EmailMultiAlternatives # <= EmailMultiAlternatives instead of EmailMessage

plain_version = 'path/to/plain_version.html' # import plain version. No html content
html_version = 'path/to/html_version.html' # import html version. Has html content

plain_message = render_to_string(plain_version, { 'context': context, })
html_message = render_to_string(html_version, { 'context': context, })

message = EmailMultiAlternatives(subject, plain_message, from_email, [to_email])
message.attach_alternative(html_message, "text/html") # attach html version
message.send()

Mis versiones simples y html se ven así: plain_version.html:

Plain text {{ context }}

html_version.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 ...
 </head>
<body>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="320" style="border: none; border-collapse: collapse; font-family:  Arial, sans-serif; font-size: 14px; line-height: 1.5;">
...
{{ context }}
...
</table>
</body>
</html>
Alkadelik
fuente