I am trying to send a pdf as a Gmail attachment in Django, which is just generated by the same view. For generating the pdf, I use to try this tutorial link.
my views.py:
def submit_report(request, pk):
template = get_template('app/pdf_rprt.html')
Industry_obj = Industry.objects.get(id=pk)
Industry_Report_obj = Industry_obj.industry_report_set.all()
report_tableA_obj = report_tableA.objects.filter(industry_report__industry=Industry_obj)
context = {
'industry' : Industry_obj,
'Industry_Report' : Industry_Report_obj,
'report_tableA' : report_tableA_obj,
}
html = template.render(context)
pdf = render_to_pdf('app/pdf_rprt.html', context)
if pdf:
to = "kanchon2199#gmail.com"
email = EmailMultiAlternatives(
#subject =
"final report sending (beta)",
#content =
'hello, this is test report sending mail',
#from email
settings.EMAIL_HOST_USER,
#list of recipent
[to]
)
email.attach_file(pdf)
email.send()
return redirect('app:index')
here the render_to_pdf comes from a custom build function in utils.py:
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None
But it says error like (for the line email.attach_file(pdf)):
TypeError at /submit_report/1/
expected str, bytes or os.PathLike object, not HttpResponse
How can I fix it?
However, I have found a solution to this problem. the return type of render_to_pdf inside the utils.py should be ContentFile like this:
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
return ContentFile(result.getvalue(), 'sampleReport.pdf')
return None
and inside views.py the email.attach_file() need dir path inside it, so we can use our database to save the pdf file and then attach it like this:
def submit_report(request, pk):
template = get_template('app/pdf_rprt.html')
context = {
'industry' : Industry_obj,
'Industry_Report' : Industry_Report_obj,
'report_tableA' : report_tableA_obj,
}
html = template.render(context)
pdf = render_to_pdf('app/pdf_rprt.html', context)
if pdf:
Final_report_obj = Final_report.objects.create(pdf=this_pdf)
Final_report_obj.save()
to = [gmail1#gmail.com, gmail2#gmail.com.....]
email = EmailMultiAlternatives(
"your Subject",
"your content.... ",
settings.EMAIL_HOST_USER,
to
)
email.attach_file(os.path.join(settings.MEDIA_ROOT, Final_report_obj.pdf.name))
email.send()
return redirect('app:index')
Here I use the Final_report_obj named model just to store my pdf file, we can use another type of model also.
Related
#adm.route('/list_users', methods=['GET'])
#login_required`enter code here`
#admin_permission.require(http_exception=403)
def list_users():
try:
if (request.content_type.startswith('application/json')):
def processjsonlist():
#csrf.exempt
try:
page = request.args.get('page', 1, type=int)
recs = current_app.config.get('RECORDS_PER_PAGE')
list_user = Person.query.order_by(
Person.id).paginate(page, recs, False)
rv = []
for person in list_user.items:
tmp = {}
tmp['id']=person.id
tmp['username'] = person.username
tmp['first_name'] = person.first_name
tmp['middle_name'] = person.middle_name
tmp['last_name'] = person.last_name
tmp['employee_code'] = person.employee_code
tmp['active'] = person.active
tmp['email'] = person.email
tmp['mobile'] = person.mobile
tmp['roles'] = [role.description for role in person.roles]
rv.append(tmp)
return jsonify({"data": rv})
except Exception as error:
return jsonify(error=repr(error))
else:
page = request.args.get('page', 1, type=int)
recs = current_app.config.get('RECORDS_PER_PAGE')
pagination = Person.query.order_by(
Person.id).paginate(page, recs, False)
search_form = SearchForm()
return render_template('adm/user_list.html', pagination=pagination,
search_form=search_form)
except Exception as e:
flash_exception(e, 'danger')
return redirect(url_for('adm.getIndex'))
You cannot render both HTML and JSON in the same response for a single request. However, you can allow the Accept header to determine whether an endpoint renders HTML or JSON. The entire response must be one or the other, but can't be mixed. I haven't used flask, so I don't know the exact details of how to do this with your project. Hopefully this will give you some terms to help with your google search.
I am trying to print invoice in PDF format in Django I used xhtml2pdf to convert HTML doc. to PDF but when I try to run my code it gives me this error :
LayoutError at /invoice/ Flowable <PmlTable#0x1D09C899130 7 rows x 5 cols(tallest row 841)> with cell(0,0) containing '<PmlKeepInFrame at 0x1d09b77d670> size=x'(538.5826771653543 x 5893.228346456693), tallest cell 841.9 points, too large on page 2 in frame 'body'(538.5826771653543 x 785.19685039370
this is in my views.py
from django.http import HttpResponse
from django.views.generic import View
from booking.utils import render_to_pdf
from django.template.loader import get_template
class GeneratePDF(View):
def get(self, request, *args, **kwargs):
template = get_template('invoice.html')
context = {
"invoice_id": 1234,
"customer_name": "John Cooper",
"amount": 1399.99,
"today": "Today",
}
html = template.render(context)
pdf = render_to_pdf('invoice.html', context)
if pdf:
response = HttpResponse(pdf, content_type='application/pdf')
filename = "Invoice_%s.pdf" %("12341231")
content = "inline; filename='%s'" %(filename)
download = request.GET.get("download")
if download:
content = "attachment; filename='%s'" %(filename)
response['Content-Disposition'] = content
return response
return HttpResponse("Not found")
and this is my urls.py
from django.urls import path
from booking.views import GeneratePDF
app_name = 'booking'
urlpatterns = [
path('invoice/', GeneratePDF.as_view(), name ="invoice"),
]
I got the answer
xhtml2pdf is not able to split table cells that are larger than the available space. To work around it you may define what should happen in this case. The -pdf-keep-in-frame-mode can be one of: “error”, “overflow”, “shrink”, “truncate” where “shrink” is the default value.
table { -pdf-keep-in-frame-mode: shrink;}
documentation link
I am trying to use wkhtmltopdf to generate a PDF and send it as an attachment via email. Here is my view:
class MYPDFView(View):
template = 'pdftemplate.html'
def get(self, request):
data = {}
response = PDFTemplateResponse(
request=request,
template=self.template,
filename="hello.pdf",
context= data,
show_content_in_browser=True,
cmd_options={'margin-top': 10,
"zoom":1,
"viewport-size" :"1366 x 513",
'javascript-delay':1000,
'footer-center' :'[page]/[topage]',
"no-stop-slow-scripts":True},
)
email = EmailMessage(
'Hello',
'Body goes here',
'from#example.com',
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
reply_to=['another#example.com'],
headers={'Message-ID': 'foo'},
attachments=[('demo.pdf', response, 'application/pdf')]
)
email.send()
return response
The error I am getting is a TypeError and it says expected bytes-like object, not PDFTemplateResponse.
I am assuming that my response variable, which I am returning to see my PDF is not the type that I am supposed to provide in the attachments attribute.
My question is, how can I convert the PDFTemplateResponse to bytes-like object before providing it in attachments triple?
return_file = "tmp/hello.pdf"
temp_file = response.render_to_temporary_file("hello.html")
wkhtmltopdf(pages=[temp_file.name], output=return_file)
email.attach_file(return_file)
Source
I want to generate a PDF using the following approach found on the Django 1.9 docs: https://docs.djangoproject.com/ja/1.9/howto/outputting-pdf.
Here is my url pattern (I don't need anything special, just a different url name like so
urlpatterns = [
url(r'^people/$', PeopleTemplate.as_view(), name='people'),
url(r'^people/pdf/$', some_view),
]
def some_view(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="example.pdf"'
p = canvas.Canvas(response)
p.drawString(100, 100, "Hello world.")
p.showPage()
p.save()
def get(self, request, *args, **kwargs):
context = locals()
context['response'] = self.response
context['p'] = self.p
return render_to_response(self.response_template, context, context_instance=RequestContext(request))
I'm trying to use a get method. This prompts for a pdf output when I hit /pdf, but doesn't contain any data - just a blank page. How do I get data that exists at this url /attendance/ to show on the pdf page when you hit the /attendance/pdf url?
I think you need to:
render html
convert it to pdf
set pdf content to response body
return response
Now your code renders template as html, adds 'application/pdf' content type to headers and returns normal html page.
You need something like PDFTemplateView. There are ready to use packages django-easy-pdf or django-wkhtmltopdf.
UPD:
def some_view(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="example.pdf"'
p = canvas.Canvas(response)
// simple but visual result is not pretty at all
for i, obj in enumerate(People.objects.all()):
p.drawString(100, 50*(i+1), str(obj))
p.showPage()
p.save()
def get(self, request, *args, **kwargs):
context = locals()
context['response'] = self.response
context['p'] = self.p
return render_to_response(self.response_template, context, context_instance=RequestContext(request))
I am trying to create a form that gets JSON inputs through a textarea(textarea1) and then processes it and prints the response back in another text area(textarea2) while still showing the original json input from textarea1. I have the code that takes the input computes the result and puts the value back. But the values are not shown in the new form. My server does not use any models.
Here is the code from views.py
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
import simplejson
import read_json
from .forms import JsonTestForm
import sys
import traceback
def index(request):
form = JsonTestForm()
return render(request, 'ellora/index.html', {'form': form})
def get_json_text(request):
print "Enter method get_json_text"
if request.method == 'POST':
print "request method is post"
form = JsonTestForm(request.POST)
if form.is_valid():
print "form is valid"
#call the read_json.py and pass the json script in the appropriate format
# capture the result of it in some way and then redirect it to the results page
try:
data_string=form.cleaned_data['jsonText']
data = simplejson.loads(data_string)
#do some processing of the data here
f = open("temp/test7777777.py", "r")
form.cleaned_data['pythonScript'] = f.read()
return render(request, "ellora/index.html", {"form": form})
except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
print ''.join('! ' + line for line in lines)
mystr = ''.join('! ' + line for line in lines)
form.cleaned_data['pythonScript'] = mystr
print "cleanded_data=", form.cleaned_data['pythonScript']
return render(request, "ellora/index.html", {"form": form})
else:
print "request type was not POST"
code from my forms.py
from django import forms
class JsonTestForm(forms.Form):
jsonText = forms.CharField(label="", widget=forms.Textarea(attrs={"class": "txtarea", "placeholder": "Enter your json script here"}), initial="[]")
pythonScript = forms.CharField(label="", widget=forms.Textarea(attrs={ "class": "txtarea", "readonly": "readonly", "rows": "1", "cols": ""}), initial="python script here")
testLog = forms.CharField(label="", widget=forms.Textarea(attrs={ "class": "txtarea", "readonly": "readonly", "rows": "1", "cols": ""}), initial="logs here")
Thanks for your help
You need to use AJAX. This should give you an idea of how to go about this. AngularJS would also allow you to easily do this, depending on the logic required.