Django e-mail template error - python

I'm trying to setup a HTML template that takes in some field forms for the subject header, and for part of the content.
views.py
if len(recipient) > 0:
messages.success(request, "Receipt sent successfully!")
subject = "Your Booking Reference: "
to = [recipient]
from_email = 'orders#demo.net'
template = get_template('booking/email/booking_reference.html')
message = EmailMessage(subject, template, from_email, ['test#test.com'])
message.content_subtype = 'html'
message.send()
return HttpResponse("Sent!")
else:
return index(request)
Whenever I request an email to be sent, I get the following error:
'Template' object has no attribute 'encode'
If I comment out message.content_subtype = 'html', I get the desired HttpResponse, but with no e-mail sent. I've added this setting to my settings.py file so that all e-mails get output to the console, but nothing is displayed
settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Try to write this :
message = EmailMultiAlternatives (subject, template, from_email, [test#test.com])
message.attach_alternative(template, "text/html")

Ok I think the problem is that you don't add a context in your template so try this:
if len(recipient) > 0:
messages.success(request, "Receipt sent successfully!")
subject = "Your Booking Reference: "
to = [recipient]
from_email = 'orders#demo.net'
template = loader.get_template('booking/email/booking_reference.html')
context = RequestContext(request, locals())
template = template.render(context)
message = EmailMessage(subject, template, from_email, ['test#test.com'])
message.content_subtype = 'html'
message.send(True)
return HttpResponse("Sent!")
else:
return index(request)

Related

Django Sending Modelform as an email

I created a site where my techs submit their inventory using model forms. Everything is working as intended but I would like to add the function of sending the whole form as an email when they submit their inventory. This would allow for my inventory team to verify counts without having to log in and check the website.
Here is my view.py I know it works if I remove the email bits and saves to my models. Currently returns an error:
'dict' object has no attribute 'splitlines'
form = Inventory_Form()
if request.method == 'POST':
form = Inventory_Form(request.POST)
tech_field = form.save(commit=False)
tech_field.technician = request.user
tech_field.save()
if form.is_valid():
form.save()
name = form.cleaned_data['initials_date']
from_email = 'operations#imbadatthis.com'
subject = 'Weekly Inventory', form.cleaned_data['initials_date']
message = form.cleaned_data
try:
send_mail(subject, message, from_email, ['myemail#n00b.com'], name)
except BadHeaderError:
return HttpResponse('Invalid header found.')
return response, redirect('inventory_submitted')
return render(request, 'inventory.html', {'form': form})
Would it be better to save the form to a csv then attach it as an email? I looked at this and also had issues with that part.
I guess the error is raised at the send_mail because of
message = form.cleaned_data
Because this is a dict and the send_mail from django expects the message to be a string.
You have to convert the dict to a string.
Maybe this helps to make a nice looking email. (documentation)

passing django object context to sendgrid email via sendgrid-python API lib

my django app has a view where accounts can send out newsletter emails to its contacts and subscribers using Sendgrid's API. sending is working with a plaintext email:
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (Mail, Subject, To, ReplyTo, SendAt, Content, From, CustomArg, Header)
def compose_email(request, send_to, *args, **kwargs):
...
if request.method == 'POST':
subject = request.POST.get('subject')
from_name = request.POST.get('from_name')
body = request.POST.get('body')
reply_to = request.POST.get('reply_to')
test_address = [request.POST.get('test_address')]
# send test email
if request.POST.get('do_test'):
if form.is_valid():
message = AccountEmailMessage(account=account, subject=subject,
from_name=from_name, destination=destination, body=body, reply_to=reply_to,
is_draft=True, is_sent=False)
message.save()
email = Mail(
subject=subject,
from_email=hi#app.foo,
html_content=body,
to_emails=test_address,
)
email.reply_to = ReplyTo(reply_to)
try:
sendgrid_client = SendGridAPIClient(settings.SENDGRID_API_KEY)
response = sendgrid_client.send(email)
message.sendgrid_id = response.headers['X-Message-Id']
message.save()
except Exception as e:
log.error(e)
messages.success(request, 'Test message has been successfully sent')
else:
messages.error(request, 'Please, check for errors')
this works. but we want to render django object properties (model fields via template tags) in an html email template from Account (account) [assume it's just a vanilla obj req query account = Account.objects.get(id=selected_account) in the view], and I'm not clear what's the recommended docs approach.
the attempt:
if request.method == 'POST':
subject = request.POST.get('subject')
from_name = request.POST.get('from_name')
body = request.POST.get('body')
reply_to = request.POST.get('reply_to')
if request.POST.get('send'):
if form.is_valid():
message = AccountEmailMessage(account=account, subject=subject,
from_name=from_name, destination=destination, body=body, reply_to=reply_to,
is_draft=False, is_sent=True)
message.save()
rendered = render_to_string('email/newsletter.html', {
'account': account,
'protocol': settings.DEFAULT_PROTOCOL,
'domain': settings.DOMAIN,
'message_body': body
})
email = Mail(
subject=subject,
from_email=hi#app.foo,
html_content=rendered,
to_emails=recipients,
mime_type='text/html'
)
email.reply_to = ReplyTo(reply_to)
try:
sendgrid_client = SendGridAPIClient(settings.SENDGRID_API_KEY)
response = sendgrid_client.send(email)
message.sendgrid_id = response.headers['X-Message-Id']
message.save()
except Exception as e:
log.error(e)
but on submit, this throws an err: NoReverseMatch: Reverse for 'account' not found. 'account' is not a valid view function or pattern name when I try to pass account as a kwarg to the context and render it as a string.
looking at the docs (https://github.com/sendgrid/sendgrid-python#use-cases) I see Mail() has a .dynamic_template_data property. that's very inefficient to process a large number of fields from the same obj, as well as attributes like image urls, and also requires use of legacy transactional templates (https://sendgrid.com/docs/ui/sending-email/create-and-edit-legacy-transactional-templates/). I see Sendgrid has a Personalization obj (https://sendgrid.com/docs/for-developers/sending-email/personalizations/) - is that the recommended way to implement this?
thanks to Iain on further testing realized we had two issues:
was attempting to encode a url in the template via {% url %} tag, that threw the NoReverseMatch
mime_type='text/html' isn't a valid kwarg for Mail(), removed that as well.
after (1) and (2) everything's working properly, no need for personalization

Django mail attachment is blank

I am trying to send a mail with an attachment using Django, the attached file is sent to the server from a user submitted form. My code is shown below
form = RequestForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.info(request, '')
subject = ''
message = ""
attachment = request.FILES['attachment']
mail = EmailMessage(subject, message, '', [''])
mail.attach(filename=attachment.name, mimetype=attachment.content_type, content=attachment.read())
mail.send()
I am receiving the mail, but the attachment in the mail is blank, i.e it doesn't contain any content. What am I missing here?
I have solved the issue, I placed the form.save() at the bottom i.e after sending the mail and the issue resolved. This is because, once we use form.save() the attachment gets stored in its path and we need to open it before we read it.
form = RequestForm(request.POST, request.FILES)
if form.is_valid():
messages.info(request, '')
subject = ''
message = ""
attachment = request.FILES['attachment']
mail = EmailMessage(subject, message, '', [''])
mail.attach(filename=attachment.name, mimetype=attachment.content_type, content=attachment.read())
mail.send()
form.save()
I believe you need to use attach_file instead of attach. attach_file allows you to pass a path, whereas you need to pass the actual data with attach. See docs.
Also, test that your attachment is actually getting uploaded, that you specified the right enctype on your form, etc. For example:
<form enctype="multipart/form-data" action="/whatever/" method="post">

Getting error 403 (CSRF token missing or incorrect)

I'm need some email form and I'm trying this:
views.py
def send_email(request):
if request.method != 'POST':
form = EmailForm()
return render_to_response('mail_form.html', {'email_form': form})
form = EmailForm(request.POST, request.FILES)
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
email = form.cleaned_data['email']
attach = request.FILES['attach']
try:
mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
mail.attach(attach.name, attach.read(), attach.content_type)
mail.send()
return render(request, 'mail_form.html', {'message': 'Sent email to %s'%email})
except:
return render(request, 'mail_form.html', {'message': 'Either the attachment is too big or corrupt'})
return render(request, 'mail_form.html', {'message': 'Unable to send email. Please try again later'})
forms.py
class EmailForm(forms.Form):
email = forms.EmailField()
subject = forms.CharField(max_length=100)
attach = forms.Field(widget=forms.FileInput)
message = forms.CharField(widget = forms.Textarea)
mail_form.html
...
{{message}}
<form method="post" action="">
{% csrf_token %}
{{ email_form.as_p }}
<input type ="submit" name = "send" value = "Send"/>
</form>
...
But constantly I get an error 403. I tried different solutions from the web, but nothing helped. What I'm doing wrong? I'm understand that something wrong with csrf in views.py, but don't understand where is the problem concretely.
Your problem is render_to_reponse. It doesn't have the context instance you can add it, but render handles this for you so why not just it instead. Also you can restructure your view to be a bit cleaner.
Here's one example.
def send_email(request):
if request.method == 'POST':
form = EmailForm(request.POST, request.FILES)
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
email = form.cleaned_data['email']
attach = request.FILES['attach']
try:
mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
mail.attach(attach.name, attach.read(), attach.content_type)
mail.send()
messages.succes(request, 'Sent an email to %s' % email)
except:
messages.error(request, 'Either the attachment is too big or corrupt')
else:
form = EmailForm()
messages.info(request, "Send an email!")
return render(request, 'mail_form.html', {'email_form': form})
Then you can use {% if messages %} in your template to display your messages to the user / iterate over them and display.
messages here is from django.contrib so you'd need to do from django.contrib import messages
Use render_to_response from django.shortcuts
Use in render_to_response context_instance = RequestContext(request)
This must fix your problem with csrf token or read https://docs.djangoproject.com/ja/1.9/ref/csrf/
Just modify your view.py like this
from django.shortcuts import render
from django.template import RequestContext
def send_email(request):
if request.method != 'POST':
form = forms.EmailForm()
return render_to_response('mail_form.html', {'email_form': form}, context_instance=RequestContext(request))
......
......
What version of django are you using?
Well, obviously you are using render() in part of your code. The problem is in your GET code--you are using render_to_response():
if request.method != 'POST':
form = EmailForm()
return render_to_response('mail_form.html', {'email_form': form})
Instead use render():
return render(request, 'mail_form.html', {'email_form': form} )
See the example in the Django docs.
The reason you need to do that is because with csrf tokens you need to:
Insert the csrf token in your form.
Include the csrf token as a cookie in the headers of the request/response.
render() accomplishes #2, but render_to_response() does not--unless you specifically tell it to, which you did not. In any case, the django 1.9 docs state:
render_to_response()
This function preceded the introduction of render() and works
similarly except that it doesn’t make the request available in the
response. It’s not recommended and is likely to be deprecated in the
future.

Attach a gererated .bib (and .ris and .txt) file to email in Python

I have a template that I populate based on a user's choices. Rather than render this template as the body of the email, I want to send it as an attachment. How do I 'write' to my .bib template (and, similarly to my .ris and .txt) that I want to use as an attachment?
views.py
# I can create this file from a link on my page but I want to send it as an email attachment??
response = render_to_response('publications/publications.bib', {'publications': publications}, context_instance=RequestContext(request), content_type='text/x-bibtex; charset=UTF-8')
response['Content-Disposition'] = 'attachment; filename="references.bib"'
return response
# email creation
text_template = 'mytemplate.txt'
html_template = 'mytemplate.html'
text_content = render_to_string(mytemplate, context, context_instance=RequestContext(request))
html_content = render_to_string(html_template, context, context_instance=RequestContext(request))
msg = EmailMultiAlternatives(subject, text_content, from_email, to)
msg.attach_alternative(html_content, "text/html")
msg.send()
new_attempt.py
# this doesn't work but maybe it's closer?
publications = request.session.get('all_publications')
response = render_to_response('publications/publications.bib', {'publications': publications}, context_instance=RequestContext(request), content_type='text/x-bibtex; charset=UTF-8')
response['Content-Disposition'] = 'attachment; filename="references.bib"'
msg.attach(filename="references.bib", content=response, mimetype="text/x-bibtex")
I finally got it using
bib_content = render_to_string(myTemplate, myContext)
msg.attach(filename="references.bib", content=bib_content, mimetype="application/x-bibtex")

Categories