Django how to send html-designed email bodies - python

On my Django project I am trying to send beautiful mails with colorful body and so on. My mail body constructing function looks like the following:
def construct_mail_body():
user = User.objects.get(id=1)
context = {
'username': user.username,
}
template = render_to_string('mail/mail_body.html', context=context)
message = strip_tags(template)
return message
mail_body.html:
{% load static %}
<h3>Hello <span style="color: red;">{{ username }}</span>,</h3>
<h3>You've successfully completed our Tutorial!></h3>
<h3>Congratulations!</h3>
But it doesn't work as expected. My mail bodies looks like:
Body 1:
Body 2 (Btw, why this happens? Why the mail is in purple?):
So how to make HTML tags work properly and is it possible to add some styling properties of css?
Thanks!
Solved:
I found a way how to do that:
from django.core.mail import EmailMessage
mail = EmailMessage(
subject,
body,
settings.EMAIL_HOST_USER,
[email],
)
mail.fail_silently = False
mail.content_subtype = 'html'
mail.send()
This way it works properly.

This is possible, but stick to hex colours, e.g. #ff0000, instead of a named colour.
Text can all go purple, as Willem commented, when something is repeated, or for replies. If you change the subject line when testing, it won't group them, and so it won't do this.
Keep using inline CSS, but stick to HTML4 and CSS2 (higher can be achieved but as a progressive enhancement for some email clients only).

Related

Django mass email - get single recipient address in every email template

I'm sending email in Django:
...
recipient_list = ['first#recipient.com', 'second#recipient.com',]
mail = EmailMessage('Subject', 'content body', [sender#email.com], recipient_list)
mail.send()
In the template I'm rendering I would like to extract single recipient like this.
template.html
...
This message was sent at {{ datetime_sent }} to address {{ recipient_address }}.
I don't want to pass recipient_list in every email in case of mass email, but rather a single recipient address. Is this possible?
You won't be able to customise this sort of information sending the same email to more than one recipient. What you will want is to use Django's mass mail function, see here:
https://docs.djangoproject.com/en/4.0/topics/email/#send-mass-mail
You can access the recipient as {{ mail.to }} in your template

Managing Urls while clicking on User profile

I'm managing to send emails to users that are present in my table and then showing success message on the same page. I'm successfully sending email to users(taking their id and then their email to send). While creating urls, I need to mention id as well which redirects me to another page. But what I want is to be redirected on the same page.
Here's the table which contains the users:
After clicking Send Mail, I'm taking the userid then email with this and then sendig mail.
Taking userid:
</button><span class="badge badge-success">Send Mail</span>
Here's my views.py code on this button:
def sendMails(request, id=None):
query_user = get_object_or_404(NewUser, id=id)
user_email = query_user.user
admin_email = request.user.email
result = send_emails(request, admin_email, user_email)
context = {
'code': result
}
return render(request,'requested_users.html', context)
And urls.py:
path('dashboard/requested_users/<id>', views.sendMails, name='sendMails'),
What I want to be on same page even after sending mail(such that the urls will be):
path('dashboard/requested_users/', views.sendMails, name='sendMails'),
But if I'm not providing the id in the urls, it's giving me the error:
Reverse for 'sendMails' with keyword arguments '{'id': 1}' not found.
I know I've asked a long question, but I'm really stuck into this.
Thank you in advance.

Request Approvals by E-mail and process it Python + Django

Maybe I am not asking the right question in the search area, but I can't find an answer for this. I am pretty sure that many people have this use case, but as a beginner in Django + Python, I need to ask it.
I have user that fills up a form and the data is stored in the database. Basically this form asks for an access to a Database and after the form is submitted I want my program to send an email to the user's manager and to the DBA to APPROVE or DENY it. Very simple, right?
My idea is that in this e-mail I send two URL's, one for approving and one for denying the request. When the URL the is clicked I send a response to the server with an update in the manager_approval field.
Has anyone implemented this solution, or could point me to something that could help me?
I am doing everything using Django + Python.
Regards,
Marcos Freccia
Basically this technique used in email verification. This is where you should look into.
Let's say you have model, named request, which has field like username to identify the person who requested access, database name, well, everything. But it will also have two "password-like" fields which will be used to determine if request was declined or not.
class Request(models.Model):
user = models.ForeignKey ...
databasename =
date =
...
access_granted = models.BooleanField(default=False)
deny_token = models.CharField()
allow_token = models.CharField()
The point is to generate those tokens on saving request in the View:
if request.method == POST:
form = RequestForm(request.POST)
if form.is_valid():
data['user'] = form.cleaned_data['user'])
data['databasename'] = form.cleaned_data['databasename'])
...
data['access_token'] = GENERATE_USING_HASH_FUNCTION()
data['deny_token'] = GENERATE_USING_HASH_FUNCTION()
form.save(data)
Then you can use module EmailMultiAlternatives to send html email like so:
subject, from_email, to = 'Request', 'admin#example.com', form.cleaned_data['manager_email']
html_content = render_to_string(HTML_TEMPLATE, CONTEXT) # Just as any regular templates
text_content = strip_tags(html_content)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], reply_to=["admin#example.com"])
msg.attach_alternative(html_content, "text/html")
msg.send()
And inside that template you construct reverse url:
{% url 'app:grant_access' allow_token=token %} # "token" you get from context
{% url 'app:deny_access' deny_token=token %} # will become example.com/deny_access/7ea3c95, where 7ea3c95 is token
Then add lines to urls.py of your app like that:
url(r'^allow_access/(?P<allow_token>[0-9]+)$', CheckAcessView.as_view(), name="app:grant_access"),
url(r'^deny_access/(?P<deny_token>[0-9]+)$', CheckAcessView.as_view(), name="app:deny_access"),]
Then create CheckAcessView view. Where you access request stored in your database and check if, for example, parameter of url "allow_token" is equal stored allow_token. If so, change request status to allowed.

Use variables in send_email()

I use html as message in one email and pass some variables like this:
subject = 'Some Subject'
plain = render_to_string('templates/email/message.txt',{'name':variableWithSomeValue,'email':otherVariable})
html = render_to_string('templates/email/message.html',{'name':variableWithSomeValue,'email':otherVariable})
from_email = setting.EMAIL_HOST_USER
send_email(subject, plain, from_email, [variableToEmail], fail_silently=False, html_message=html)
That works good but now I need to take the message content from one table from the database, the table have three columns, in the first register have this values in each column. Column subject have Account Info, column plain have Hello {{name}}. Now you can access to the site using this email address {{email}}. and the column html have <p>Hello <strong>{{name}}</strong>.</p> <p>Now you can access to the site using this email address <strong>email</strong>.</p>.
So to take the values from the database I do this obj = ModelTable.objects.get(id=1) then this:
subject = obj.subject
plain = (obj.plain,{'name':variableWithSomeValue,'email':otherVariable})
html = (obj.html,{'name':variableWithSomeValue,'email':otherVariable})
from_email = setting.EMAIL_HOST_USER
send_email(subject, plain, from_email, [variableToEmail], fail_silently=False, html_message=html)
But this give me the error
AttributeError: 'tuple' object has no attribute 'encode'
so I tried to passing .encode(´utf-8´) for the values and gives me the same error, then change the value for each variable and find that the problem comes from plain = (obj.plain,{'name':variableWithSomeValue,'email':otherVariable}) and html = (obj.html,{'name':variableWithSomeValue,'email':otherVariable}) so I think that I passing the variables in the wrong way, so How can I do it in the right way? or maybe is for the encoding of the database but I think that using .encode(utf-8) should solve that problem but I really think that I pass the variables name and email in the wrong way.
Sorry for the long post and my bad grammar, if need more info please let me know.
I'm assuming that obj.plain and obj.html are strings representing your templates (as stored in the database)?
If that is the case, then you still need to render your email content. However, instead of using render_to_string, which takes as it's first argument a template path, you will want to create a template based on your string, and then render that template. Consider something like the following:
...
from django.template import Context, Template
plain_template = Template(obj.plain)
context = Context({'name':variableWithSomeValue,'email':otherVariable})
email_context = plain_template.render(context)
...
send_email(...)
Here's a link that better explains rendering string templates, as opposed to rendering template files.
https://docs.djangoproject.com/en/1.7/ref/templates/api/#rendering-a-context

Sending email with html in Django 1.7

In send_mail() we have one new parameter - html_message. Docs
I have email.html file and I want to send html version of my message. I can't find any example for Django 1.7.
Can you show me a way, how to do this? Does I need to use os.open() my html file?
Thanks!
render_to_string : which loads a template, renders it and returns the resulting string.
html_message : If html_message is provided, the default message replaced with Html message.
mail/html-message.html
Hi {{ first_name }}.
This is your {{ email }}
Thank you
views.py
def mail_function(request):
subject = 'Test Mail'
from = 'info#domain.com'
to = 'to#domain.com'
c = Context({'email': email,
'first_name': first_name})
html_content = render_to_string('mail/html-message.html', c)
txtmes = render_to_string('mail/text-message.html', c)
send_mail(subject,
txtmes,
from,
[to],
fail_silently=False,
html_message=html_content)
Tim,
You don't need OS.open. You can do this by creating an html template first, and importing it using the get_template method. In your
view, add something along the lines of :
app/view.py
from django.core.mail import EmailMultiAlternatives
from django.http import HttpResponse
from django.template.loader import get_template
def send_mail(request):
text = get_template('email_template.txt')
html = get_template('email_template.html')
data = {'templating variable': data_var}
# If Client cant receive html mails, it will receive the text
# only version.
# Render the template with the data
content_txt = text.render(data)
content_html = html.render(data)
# Send mail
msg = EmailMultiAlternatives(subject, content_text, from_email, [to])
msg.attach_alternative(content_html, "text/html")
msg.send()
Note: You don't need Context for Djange 1.10+. In Django 1.8+, the template's render method takes a dictionary for the context parameter. Support for passing a Context instance is deprecated, and gives an error in Django 1.10+.

Categories