Send html email using flask in Python - python

I want to send HTML webpage as a mail body using Python and Flask. I tried using MIME module, but somehow I am not being able to send mail. If anyone has any expertise on this, can you please help me.
It would be great if you could provide some code as well.

flask-mail is a good tool that I use to render the template to render HTML as the body of my mail.
from flask_mail import Message
from flask import render_template
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
def send_mail_flask(to,subject,template,**kwargs):
msg = Message(subject=subject,sender='email#ofTheSender.com', recipients=to)
msg.body = render_template('template.txt', **kwargs)
msg.html = render_template('template.html', **kwargs)
mail.send(msg)
The template is the HTML file you wish to include in your mail and you can also add a text version using msg.body!
You may need to add more environment variables according to the SMTP service you use.

I recently made a pretty awesome email library and I decided to make a Flask extension for it as well: Flask-Redmail
Install
pip install flask-redmail
Configuration
from flask import Flask
from flask_redmail import RedMail
app = Flask(__name__)
email = RedMail(app)
# Setting configs
app.config["EMAIL_HOST"] = "localhost"
app.config["EMAIL_PORT"] = 0
app.config["EMAIL_USERNAME"] = "me#example.com"
app.config["EMAIL_PASSWORD"] = "<PASSWORD>"
app.config["EMAIL_SENDER"] = "no-reply#example.com"
Simple Example
You may send emails simply:
#app.route("/send-email")
def send_email():
email.send(
subject="Example email",
receivers=["you#example.com"],
html="""
<h1>Hi,</h1>
<p>this is an example.</p>
"""
)
Template Example
It can also use your default Jinja env. Create a file emails/verify.html to your Flask template folder that looks like this:
<h1>Hi, you visited {{ request.endpoint }}.</h1>
Then use this template:
#app.route("/send-email-template")
def send_email():
email.send(
subject="Template example",
receivers=["you#example.com"],
html_template="emails/verify.html"
)
Flask Redmail is quite a simple wrapper for Red Mail and Red Mail is pretty extensive. It can:
Send attachments from various types
Embed images and plots to the HTML
Embed prettified tables to the HTML
Supports Jinja out-of-the-box
Gmail and Outlook are preconfigured
I hope you find it useful.
Links:
Documentation
Source Code
Releases
Red Mail:
Documentation
Source Code
Releases

Try using FlaskMail https://pythonhosted.org/flask-mail/
msg = Message(
recipients=[''],
sender='xx#zz.yy',
reply_to='aa#bb.cc',
subject=mail.subject
)
msg.html = mail.body
mail.send(msg)
here, mail is an imported file from "mails" directory,
and body is an HTML markup.

On a separate python file (emails.py) I created an email class that contains all the emails (as class methods) to be sent to the user based on his actions as below:
class Email:
def accountCreation():
body = "<html> <body> <p> {} </p> <br> <br> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p> </body> </html>"
return body
then on another file (app.py) where I made use of flask_mail library I have:
from flask_mail import Mail, Message
import emails
mailer = emails.Email()
try:
email = "johndoe#mail.com"
body = mailer.accountCreation()
msg = Message('Hello',recipients=[email])
msg.html = body
mail.send(msg)
except Exception as e:
print(e)

Firstly, ensure there's a folder in your apps directory called templates. This is where all your html templates should be.
**server.py**
from flask-mail import Mail, Message
from flask import Flask
import os
app = Flask(__name__)
send_mail = Mail(app)
context={
"fullname": fullname,
"otp_code": otp_code,
"year": datetime.now().year,
"subject":"Registration Mail",
}
email_message = Message(
subject=mail_subject,
sender=os.getenv("ADMIN_EMAIL"),
recipients=[receipients_email],
html=render_template(template_name_or_list="otp.html", **context)
)
send_mail.send(email_message)
where "otp.html" is an existing html template in your templates folder

Related

Sending a text + HTML email with a calendar ICS attachment in Django or Python

I've been searching for a library or at least functional snippet of code that lets me send an e-mail from Django (or at least in Python) with text content, HTML content, and an ICS calendar attachment that is recognized by every major e-mail client. For my particular use case it's enough if the user is offered an 'add to calendar' button.
I feel like this should be a solved problem by now, but I'm only finding answers that refer to libraries that aren't being maintained or that are outdated or incomplete in some other way. I've tested a couple of snippets that will attach an ICS file, but G-mail doesn't give me the option of adding it to the calendar like it usually does.
Is there a ready made solution that I'm missing?
Start by creating a .ics file, this can ofcourse also be done by a python script to generate dynamic .ics files.
.ics file
More information about iCalendar: basic information and general templates (see example below).
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
UID:uid1#example.com
DTSTAMP:19970714T170000Z
ORGANIZER;CN=John Doe:MAILTO:john.doe#example.com
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
GEO:48.85299;2.36885
END:VEVENT
END:VCALENDAR
You can also generate your own iCalendar if you find that easier (see generated example below).
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ical.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20201011T015911Z
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20210716T221958Z
UID:20210716T221958Z-901688629#marudot.com
DTSTART;TZID=Europe/Berlin:20210717T120000
DTEND;TZID=Europe/Berlin:20210717T160000
SUMMARY:Stack Overflow
DESCRIPTION:iCalendar example for Stack Overflow user Ciske\n
LOCATION:Amsterdam
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Stack Overflow
TRIGGER:-PT1H
END:VALARM
END:VEVENT
END:VCALENDAR
Django settings.py
EMAIL_HOST = SMTP server.
EMAIL_HOST_USER = Login credentials for the SMTP server.
EMAIL_HOST_PASSWORD = Password credential for the SMTP server.
EMAIL_PORT = SMTP server port.
EMAIL_USE_TLS or _SSL = True if secure connection.
Django views.py
Send email with .ics file as attachment.
from django.core.mail import EmailMessage
# Send email with attachment
email = EmailMessage(
'Subject',
'Email body',
'from#example.com',
['to#example.com']
)
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()
You can even add an .html file to your email.
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
html_content = render_to_string('assets/email.html', context)
email = EmailMultiAlternatives('Subject', 'Email body', 'from#example.com', [to#example.com])
email.attach_alternative(html_content, 'text/html')
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()
Django has built-in solution based on python's smtplib:
https://docs.djangoproject.com/en/3.2/topics/email/
You need to provide some credentials to your smtp server(gmail, mailgun etc) in settings and after that you can use django.core.mail module.
For attaching stuff you can use EmailMessage.attach() or attach_file()
https://docs.djangoproject.com/en/3.2/topics/email/#django.core.mail.EmailMessage
So the key was to attach the ICS file as a file, not as a string (using django.core.mail.message.EmailMessage.attach_alternative()).
The following snippet works for me in Gmail, Hotmail, and Yahoo mail (MS Outlook to be confirmed), meaning the calendar event information is shown together with the email, and at least Gmail and Hotmail provide an option to add the event to your calendar.
from django.core.mail.message import EmailMultiAlternatives # At the top of your .py file
email = EmailMultiAlternatives(subject, message, settings.FROM_EMAIL, ['recipient#email.here'])
# email.attach_alternative('<b>html here</b>', 'text/html') # Optional HTML message
email.attach_file(filename_event, 'text/calendar')
email.send(fail_silently=False)
I'm using ics https://pypi.org/project/ics/ to create the ICS file. This package is currently still being maintained. The only other major Python ics file library I could find is ical https://pypi.org/project/icalendar/ and the source for that hasn't been updated in a year as of sep 1st, 2021.
This code works for me to create the ics file:
from ics import Calendar, Event # At the top of your .py file
ICS_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
calendar = Calendar()
event = Event()
event.name = _("Our event name")
event.begin = appointment.start_time.strftime(ICS_DATETIME_FORMAT)
event.end = appointment.end_time.strftime(ICS_DATETIME_FORMAT)
event.organizer = settings.DEFAULT_FROM_EMAIL
calendar.events.add(event)
filename_event = 'invite-%d.ics' % appointment.id
with open(filename_event, 'w') as ics_file:
ics_file.writelines(calendar)
where appointment is my own Django class, of which start_time and end_time are of the type DateTimeField.
If you create a new ics file per request, it's important to also have a unique filename per request, so you don't risk two separate requests writing to the file simultaneously.
After sending the ICS file I'll delete it like so:
import os # At the top of your .py file
os.remove(filename_event)
You can try using mark_safe() it renders the js,html code as a string to a html page .
I had used it for customizing Django admin.
Check the example below :
some_sample_string = '''<h1>This is some sample </h1>'''
my_sample_html = mark_safe(some_sample_string)
you can design a page in HTML add some custom designing to it and then return the mark_safe byte object to HTML or any web page and tag it there it will work.
you can check these links it might be a help for you
https://www.kite.com/python/docs/django.utils.safestring.mark_safe
Return mark_safe string from __str__
https://www.fullstackpython.com/django-utils-safestring-mark-safe-examples.html
https://docs.djangoproject.com/en/3.0/_modules/django/utils/html/

SendGrid email displayed as plaintext instead of HTML

I am sending a dynamic email template through SendGrid using python. The correct HTML email template is displayed in inbox when I test the email through Sendgrid UI, but a plaintext version is displayed when the email is sent via python . Here's the code:
import sendgrid
import os
from sendgrid.helpers.mail import *
from sendgrid import SendGridAPIClient
sg = sendgrid.SendGridAPIClient('MY_KEY_HERE')
from_email = Email("example#email.com")
to_email = To("example#email.com")
subject = "Sending with SendGrid is Fun"
content = Content("text/plain", "and easy to do anywhere, even with Python")
mail = Mail(from_email, to_email, subject, content)
mail.template_id = "d-9e2812d070bb458e8d6cbd6c9b8922f6"
mail.dynamic_template_data = {
"first_name": "John",
"last_name": "Blacutt",
}
response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)
Here's the plaintext email:
If you are using a stored template and you want your dynamic content to render as HTML, you need to add an extra set of handlebars in the template itself else it will render as plain text.
EX:
<p>
{{{MailBody}}}
</p>
Notice the extra handlebars around {{MailBody}}

Azure functions to send email with logo using sendgrid

I am sending email from Azure functions using python and sendgrid. I am able to form the other part of the email but when I am trying to insert logo in signature of the email is not displaying that. Rest HTML is rendering fine.(till href)
Following is my code :
import sendgrid
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
message = Mail(
from_email=email_from,
to_emails=email_to,
subject=email_subject,
html_content= '<br>Hi '+name+ ','+
'<br>body of the email. Working fine.' +
'<br><a href='+no+'>No'</a>\n' +
'<br><br>LOGO--><img src="doc.png" alt="W3Schools.com"/>'+
'</html>'
)
response = sg.send(message)
print(response.status_code)
I tried pointing out URL in src too and it did not help.
There are 3 options to achieve this:
CID Embedded Images (Inline Images)
Inline Embedding (Base64 Encoding)
Linked Images
Check out below links for implementation details:
https://sendgrid.com/blog/embedding-images-emails-facts/
https://github.com/sendgrid/sendgrid-python/issues/736

how to use designed template in django using sendgrid API?

I have integrate the send-grid with my Django application and mails also sent successfully. But now I want to send email with designed template from my django application. I have read the docs also, but not get any idea how to use it programatically. This is my first time to use send-grid. Please can anyone help me to find out the way how can I send send-grid template from django application.
You can use SendGrid's Template Engine to store a template inside SendGrid. You then reference that template ID when sending an email via the SendGrid API, you can see an example of that code in the sendgrid-python library.
Here it is in a full example, it uses a SendGrid API key (you can find out how to get that set up by reading this guide):
import sendgrid
sg = sendgrid.SendGridClient('sendgrid_apikey')
message = sendgrid.Mail()
message.add_to('John Doe <john#email.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Doe John <doe#email.com>')
# This next section is all to do with Template Engine
# You pass substitutions to your template like this
message.add_substitution('-thing_to_sub-', 'Hello! I am in a template!')
# Turn on the template option
message.add_filter('templates', 'enable', '1')
# Tell SendGrid which template to use
message.add_filter('templates', 'template_id', 'TEMPLATE-ALPHA-NUMERIC-ID')
# Get back a response and status
status, msg = sg.send(message)
You need Sendgrid-Python interface first:
pip install sendgrid
after that try this:
import os
from sendgrid.helpers.mail import Mail
from sendgrid import SendGridAPIClient
FROM_EMAIL = 'Your_Name#SendGridTest.com'
# update to your dynamic template id from the UI
TEMPLATE_ID = 'd-d027f2806c894df38c59a9dec5460594'
# list of emails and preheader names, update with yours
TO_EMAILS = [('your_email#domain.com', 'Sourabh MbeforL'),]
def SendDynamic():
message = Mail(
from_email=FROM_EMAIL,
to_emails=TO_EMAILS)
# pass custom values for our HTML placeholders
message.dynamic_template_data = {
'subject': 'SendGrid Development',
'place': 'New York City',
'event': 'Twilio Signal'
}
message.template_id = TEMPLATE_ID
try:
sg = SendGridAPIClient(os.environ.get('YOUR_SENDGRID_API_KEY')) ## (^_^) This face is just to grab your attention for api key needed
response = sg.send(message)
code, body, headers = response.status_code, response.body, response.headers
print(f"Response code: {code}")
print(f"Response headers: {headers}")
print(f"Response body: {body}")
print("Dynamic Messages Sent!")
except Exception as e:
print("Error: {0}".format(e))
return str(response.status_code)

How can I send HTML email via Celery? It keeps sending in text/plain

I setup a system Django/Celery/Redis. And I used EmailMultiAlternatives to send my HTML and Text email.
When I send email as part of the request process, the email is sent in HTML. All runs well and it wraps around a function. Here's the code:
def send_email(email, email_context={}, subject_template='', body_text_template='',
body_html_template='', from_email=settings.DEFAULT_FROM_EMAIL):
# render content
subject = render_to_string([subject_template], context).replace('\n', ' ')
body_text = render_to_string([body_text_template], context)
body_html = render_to_string([body_html_template], context)
# send email
email = EmailMultiAlternatives(subject, body_text, from_email, [email])
email.attach_alternative(body_html, 'text/html')
email.send()
However, when I tried to run it as a Celery Task, like below, it just sent as "text/plain". What could be the problem? Or what can I do to find out more? Any hint or solution are greatly appreciated.
#task(name='tasks.email_features', ignore_result=True)
def email_features(user):
email.send_email(user.email,
email_context={'user': user},
subject_template='emails/features_subject.txt',
body_text_template='emails/features_body.txt',
body_html_template='emails/features_body.html')
Celery does not affect the executing results of tasks. Have you restarted celeryd after make change to the task? It's important for celery to reload the Python code.
When you were using EmailMultiAlternatives and email.attach_alternative(body_html, 'text/html'), the email was sent in the Content-Type: multipart/alternative; and the text/html is an alternative one, it depends on mail receipts to choose the content type of the mail during the rendering. So is the receipt same one between the view procedure and celery procedure?
You could output the sending mail directly, through python -m smtpd -n -c DebuggingServer localhost:25 to find out the actual messages. I've tested on my mac w/ redis-backed Celery, the outputs of the examples taken from the official doc are same as expected.
from django.core import mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
class SendEmail(Task):
name="send_email"
def run(self,email):
subject = 'Daily News Letter'
html_message = render_to_string('letter.html', {'context': 'values'})
plain_message = strip_tags(html_message)
from_email = env('EMAIL_HOST_USER')
mail.send_mail(subject, plain_message, from_email, [email], html_message=html_message)
return None
send_email = celery_app.register_task(SendEmail())

Categories