Création de modèles d'e-mails avec Django

207

Je veux envoyer des e-mails HTML, en utilisant des modèles Django comme celui-ci:

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

Je ne trouve rien send_mail, et django-mailer envoie uniquement des modèles HTML, sans données dynamiques.

Comment utiliser le moteur de modèle de Django pour générer des e-mails?

Anakin
la source
3
Avis de Django 1.7offres html_messageà send_email stackoverflow.com/a/28476681/953553
andilabs
Salut @anakin, je lutte depuis longtemps avec ce problème et j'ai décidé de créer un package pour cela. Je serais très heureux d'avoir vos commentaires: github.com/charlesthk/django-simple-mail
Charlesthk

Réponses:

385

À partir des documents , pour envoyer des e-mails HTML, vous souhaitez utiliser d'autres types de contenu, comme ceci:

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()

Vous aurez probablement besoin de deux modèles pour votre e-mail - un texte brut qui ressemble à ceci, stocké dans votre répertoire de modèles sous email.txt:

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

et un HTMLy, stocké sous email.html:

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

Vous pouvez ensuite envoyer un e-mail à l'aide de ces deux modèles en utilisant get_template, comme ceci:

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
la source
40
Je pense que vous pouvez simplifier cela avec render_to_string , ce qui vous permettrait de perdre les lignes distinctes affectant les modèles à plaintextet htmly, et de simplement définir des modèles et des contextes lorsque vous définissez text_contentet html_content.
cms_mgr
@cms_mgr Pouvez-vous élaborer ce que vous voulez dire et comment nous pouvons l'utiliser
akki
3
@akki voir la réponse d'Andi ci-dessous, ce qui simplifie également la partie alternative grâce à l'ajout du paramètre html_message à send_email () dans Django 1.7
Mike S
Pardonnez-moi, mais pourquoi utilisons-nous txt et htmly les deux en même temps pour un courrier. Je n'ai pas compris cette logique
Shashank Vivek
ce ne sont que des exemples pour montrer différents types de méthodes, vous pouvez utiliser n'importe laquelle d'entre elles @ShashankVivek
erdemlal
242

Garçons et filles!

Depuis la méthode 1.7 de Django dans send_email , le html_messageparamètre a été ajouté.

html_message: si html_message est fourni, l'e-mail résultant sera un e-mail en plusieurs parties / alternatif avec un message comme type de contenu text / plain et html_message comme type de contenu text / html.

Vous pouvez donc simplement:

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
la source
1
Remarquez si 'email.txt' et 'email.html' sont dans un modèle de répertoire tel que défini dans les paramètres que juste render_to_string ('email.txt', {'some_params': some_params} _
Bruno Vermeulen
Merci pour l' render_to_stringastuce, très pratique.
hoefling le
1
Bonne solution! Cependant, avec send_mailn'est pas possible de définir un en-tête personnalisé comme ie Return-Pathqui peut être défini avec leEmailMultiAlternatives's constructor header parameter
Qlimax
26

J'ai créé django-templated-email dans le but de résoudre ce problème, inspiré par cette solution (et la nécessité, à un moment donné, de passer de l'utilisation de modèles django à l'utilisation d'un ensemble mailchimp, etc. de modèles pour les e-mails transactionnels et modèles pour mon propre projet). C'est toujours un travail en cours, mais pour l'exemple ci-dessus, vous feriez:

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

Avec l'ajout de ce qui suit à settings.py (pour compléter l'exemple):

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

Cela recherchera automatiquement les modèles nommés 'templated_email / email.txt' et 'templated_email / email.html' pour les parties plain et html respectivement, dans les répertoires / chargeurs de modèle django normaux (se plaindre s'il ne peut pas en trouver au moins un) .

Darb
la source
1
Cela me semble correct. J'ai réduit cela et l'ai jeté dans un ticket pour ajouter django.shortcuts.send_templated_mail: code.djangoproject.com/ticket/17193
Tom Christie
Cool, heureux de le voir proposé comme outil pour le noyau django. Mon cas d'utilisation / focus pour la lib est un peu plus grand que le simple raccourci, (basculement facile entre les fournisseurs de messagerie qui ont des API de clé / valeur pour l'envoi de courrier), mais cela semble être une fonctionnalité manquante du noyau
Darb
15

Utilisez EmailMultiAlternatives et render_to_string pour utiliser deux modèles alternatifs (un en texte brut et un 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
la source
5

J'ai créé Django Simple Mail pour avoir un modèle simple, personnalisable et réutilisable pour chaque e-mail transactionnel que vous souhaitez envoyer.

Le contenu et les modèles des e-mails peuvent être modifiés directement depuis l'administrateur de django.

Avec votre exemple, vous enregistreriez votre email:

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)

Et envoyez-le de cette façon:

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)

J'adorerais avoir des commentaires.

Charlesthk
la source
Cela aiderait beaucoup si vous téléchargez une application de démonstration de votre package sur votre dépôt.
ans2human
Salut @ ans2human merci pour cette suggestion, je l'ajoute à la liste des améliorations!
Charlesthk
3

Il y a une erreur dans l'exemple .... si vous l'utilisez tel qu'il est écrit, l'erreur suivante se produit:

<type 'exceptions.Exception'>: l'objet 'dict' n'a pas d'attribut 'render_context'

Vous devrez ajouter l'importation suivante:

from django.template import Context

et changez le dictionnaire pour qu'il soit:

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

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

idbill
la source
Merci - c'est réglé maintenant.
Dominic Rodger
3

Django Mail Templated est une application Django riche en fonctionnalités pour envoyer des e-mails avec le système de modèles Django.

Installation:

pip install django-mail-templated

Configuration:

INSTALLED_APPS = (
    ...
    'mail_templated'
)

Modèle:

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

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

Python:

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

Plus d'informations: https://github.com/artemrizhov/django-mail-templated

raacer
la source
C'était vraiment facile à utiliser. Merci.
cheenbabes du
Bonjour, comment puis-je définir tous mes destinataires sur BCC?
aldesabido
@aldesabido Ceci est juste un wrapper autour de la classe EmailMessage standard de Django. Vous devriez donc lire la documentation officielle lorsque vous recherchez de telles fonctionnalités: docs.djangoproject.com/en/1.10/topics/email Jetez également un coup d'œil à une question similaire: stackoverflow.com/questions/3470172/…
raacer
Pour être plus précis, l'EmailMessage standard n'est pas encapsulé, mais hérité. C'est-à-dire que c'est l'extension pour la classe standard :)
raacer
Possible d'inclure JS / CSS dans le modèle?
Daniel Shatz
3

Je sais que c'est une vieille question, mais je sais aussi que certaines personnes sont comme moi et recherchent toujours des réponses actualisées, car les anciennes réponses peuvent parfois avoir des informations obsolètes si elles ne sont pas mises à jour.

C'est maintenant janvier 2020, et j'utilise Django 2.2.6 et Python 3.7

Remarque: J'utilise DJANGO REST FRAMEWORK , le code ci-dessous pour envoyer des e-mails était dans un ensemble de vues de modèle dans monviews.py

Donc, après avoir lu plusieurs belles réponses, c'est ce que j'ai fait.

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)

Important! Alors, comment render_to_stringobtient-on receipt_email.txtet receipt_email.html? Dans mon settings.py, j'ai TEMPLATESet voici à quoi ça ressemble

Faites attention à DIRS, il y a cette ligne os.path.join(BASE_DIR, 'templates', 'email_templates') .Cette ligne est ce qui rend mes modèles accessibles. Dans mon project_dir, j'ai un dossier appelé templateset un sous-répertoire appelé email_templatescomme ça project_dir->templates->email_templates. Mes modèles receipt_email.txtet receipt_email.htmlsont sous le sous- email_templatesrépertoire.

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',
        ],
    },
},
]

Permettez-moi d'ajouter cela, mon recept_email.txtapparence ressemble à ceci;

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

Et, mon receipt_email.htmlressemble à ceci;

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

J'ai écrit un extrait qui vous permet d'envoyer des e-mails rendus avec des modèles stockés dans la base de données. Un exemple:

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

Si vous souhaitez des modèles d'e-mails dynamiques pour votre courrier, enregistrez le contenu de l'e-mail dans vos tables de base de données. C'est ce que j'ai enregistré en tant que code HTML dans la base de données =

<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>

Selon vous:

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()

Cela enverra au modèle HTML dynamique ce que vous avez enregistré dans Db.

Javed
la source
0

send_emai()n'a pas fonctionné pour moi, donc j'ai utilisé EmailMessage ici dans les django docs .

J'ai inclus deux versions de l'anser:

  1. Avec la version e-mail html uniquement
  2. Avec les versions e-mail en texte brut et e-mail 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 vous souhaitez inclure une version en texte brut de votre e-mail, modifiez ce qui précède comme ceci:

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()

Mes versions plain et html ressemblent à ceci: 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
la source