How to convert html to pdf in Django? - python

I'm starting with Django and I working with HTML and I would like to convert to pdf.
I have this view which I get the data registered in my DB by id:
def contrato(request, id):
return render(request,'contrato\contrato.html', {'asociado': get_queryset(id)})
This renders me the following html, it is something simple:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CPS</title>
</head>
<body>
<h1>Contrato de Prestación de Servicios</h1>
<div>
<ul>
{% for dato in asociado %}
<li>Función/Título: {{ dato.job_title }}</li>
<li>nombre completo: {{ dato.first_name }} {{ dato.last_name }}</li>
<li>Email: {{ dato.email }}</li>
<li>RFC: {{ dato.rfc }}</li>
<li>CURP: {{ dato.curp }}</li>
<li>Clabe: {{ dato.interbank_key }}</li>
<li>País: {{ dato.country }}</li>
<li>Status: {{ dato.status }}</li>
<li>Creado: {{dato.created}}</li>
{% endfor %}
</ul>
</div>
</body>
</html>
How can I convert this html to pdf with the registered data to download. I have only achieved an empty pdf (without the data) or only with the H1 header.
I will appreciate any help!

You can use wkhtml2pdf, which will need a HTML document and will generate a pdf file,then return to the generated file to the user, something like this
def contrato(request, id):
HTML = render(request,'contrato\contrato.html', {'asociado': get_queryset(id)}).content
f = open("/tmp/report.html","w")
f.write(HTML)
f.close()
import subprocess
subprocess.run("wkhtml2pdf /tmp/report.html /tmp/report.pdf")
return FileStream(open("/tmp/report.pdf","rb"), as_attachment=True)

Doing this without an external library is going to be pretty complicated and involves transpiling the HTML to PDF.
Instead, you'll probably want to use a library like xhtml2pdf.
First pip install xhtml2pdf. Then update your controller function:
from xhtml2pdf import pisa
# ...
def contrato(request, id):
if request.path_info.split('.')[-1] == "pdf"
return render_pdf(request, 'contrato/contrato.html', {'asociado': get_queryset(id)})
return render(request, 'contrato\contrato.html', {'asociado': get_queryset(id)})
def render_pdf(request, template_path, context)
filename = f'{template_path.split('.')[0]}.pdf'
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="{filename}"'
template = get_template(template_path)
html = template.render(context)
pisa_status = pisa.CreatePDF(html, dest=response)
if pisa_status.err:
return HttpResponse(f'We had some errors <pre>{html}</pre>')
return response
For more advanced use cases, you'll probably want to refer to the documentation linked above.

Related

im having trouble Django materialize css form when a render the form i get the variable does not exist from field.html from django-materializecss-form

I'm new to Django. whenever I render the form I get "Exception has occurred: VariableDoesNotExist
Failed lookup for key [required_css_class] in " . I don't understand this error if anybody explain or tell me what I'm doing wrong will be much appreciated
thank you in advance
this is my view
def considerations(request):
if request.method == "POST":
form = B2bConsideration(request.POST)
v = form.is_valid()
if form.is_valid():
instance = form.save(commit=True)
#adding date to instance from request not in table but a good idea
#instance.date = request.date
instance.save()
return HttpResponseRedirect(reverse('b2b:TypeOfTitle'))
else:
return HttpResponse(form.errors)
else:
form = B2bConsideration()
return render(request, 'b2b/B2B_notes.html',{'form':form} )
this is my modelform
class B2bConsideration(ModelForm):
CHOICES = [('yes_title_under_name', 'yes'),('No_title_under_name','no'),]
under_name_title = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect())
class Meta:
model = Consideration
fields = ['under_name_title','salvage_title','is_title_paidoff']
this is my model
under_Name_choices = [('yes_title_under_name', 'yes'),('No_title_under_name','no'),]
salvage_title_choices =[('yes_salvage_title','yes'),('no_salvage_title','no'),]
is_title_paidoff_choices = [('yes_title_paidoff', 'yes'),('no_title_paidoff','no'),]
class Consideration (models.Model):
under_name_title = models.CharField(max_length=21, choices=under_Name_choices)
salvage_title = models.CharField(max_length=18, choices=salvage_title_choices)
is_title_paidoff = models.CharField(max_length=21, choices=is_title_paidoff_choices)
here is where the error points to. this is what it said "Exception has occurred: VariableDoesNotExist
Failed lookup for key [required_css_class] in "
<label class="control-label {{ classes.label }} {% if field.field.required %}{{ form.required_css_class }}{% endif %}">{{ field.label }}</label>
this is my HTML
{% load static %}
{% load materializecss %}
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Titlemax B2B</title>
{% block css %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
{% endblock css %}
{% block javascript %}
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
{% endblock javascript %}
<script src="{% static 'B2B_index.js' %}"></script>
<link rel="stylesheet" href="{% static 'B2B_index.css' %}">
</head>
{{ form.under_name_title.0}}
{{ form.under_name_title.1}}
{{ form|materializecss:'m6' }}
I think the problem is that you are overriding the variable "form" by passing it to your template with the line:
return render(request, 'b2b/B2B_notes.html',{'form':form} )
Try renaming your variable to something other than 'form', such as:
return render(request, 'b2b/B2B_notes.html',{'my-form':form} )
Make sure to also rename the variable in your html in the last 3 lines where you call {{ form }}

Folium map in Django only says 'None'

I am trying to create map in Django using folium. There is no error message, but instead of the map it displays the word 'None'.
I have a django web app that reads from a postgressql database and displays the records. That is working. I want to add a map with markers, and the markers will get coordinates from the postgresql data. When I try to create a map using iframe, I get the word 'None'. I have tried just hard coding a starting location to just create the map. I still get the word 'None'.
I also typed the hard coded coordinates into google maps, and made a flask app that creates the map. They show up fine.
views.py:
from django.shortcuts import render
from django.http import HttpResponse
from .models import PhotoInfo
import folium
import pandas
# Create your views here.
def index(request):
VarPhotoInfo = PhotoInfo.objects.order_by('DateTaken')
context = {'PhotoInfo': VarPhotoInfo }
return render(request,'natureapp/index.html',context)
def show_map(request):
#creation of map comes here + business logic
#PhotoInfo1 = PhotoInfo.objects.order_by('DateTaken')
map = folium.Map(location=[33.57137166666667, -117.76325])
map.save("natureapp/map.html")
context = {'my_map': map }
return render(request, 'natureapp/map.html', context)
map.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NatureMapper</title>
</head>
<h1>Map goes here </h1>
{{ my_map.render }}
</html>
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NatureMapper</title>
</head>
<h1>Here it is! </h1>
{% if PhotoInfo %}
{% for Photo in PhotoInfo %}
<p>there is info.</p>
<p> {{ Photo.PhotoName }}</p>
<p> {{ Photo.Lat }}</p>
<p> {{ Photo.Long }}</p>
<p> <img src="{{ Photo.PhotoImage.url }}" width = "240" alt=""></p>
{% endfor %}
{% else %}
<p>there is no info.</p>
{% endif %}
<iframe id="encoder_iframe" height=95% width="90%" src="{% url 'show_map' %}">
</iframe>
</html>
There is no error message. Just the word 'None'

Insert python string into Django template context with markup

If I render a static image in the .html template it works. But if I provide the static markup string as dictionary value to the template (the context), it will not work. It seems to be something to do with string formatting and not allowing me to use {% %} the way I need to. I have tried:
1. .format()
2. escaping the percent characters
3. raw strings
4. concatenation
5. autoescape
6. | safe
and a number of other things
Basically, I am constructing a multi-line string in view.py with '''{% %}''', and then rendering a template with this string as the context. Python 2.
UPDATE
Simple non-working example:
view.py
def index(request):
image_insert = ''
images = ['image1.jpg', 'image2.jpg', 'image3.jpg']
for image in images:
image_insert += '<img src="{}">'.format(image)
context = {'insert': image_insert}
template = loader.get_template('index.html')
return HttpResponse(template.render(context, request))
index.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML File</title>
</head>
<body>
First Image
<img src={% static "image.jpg" %}>
Second Image <!-- does not work -->
{{ image_insert | safe }}
</body>
</html>
Page Source:
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML File</title>
</head>
<body>
<img src=/static/mqdefault.jpg>
Second Image
<img src="image1.jpg"><img src="image2.jpg"><img src="image3.jpg">
</body>
</html>
Obviously, there is a difference. This is Django 1.11 btw if it makes a difference.
You can also achieve this by passing img source from the view as follow:
views.py
from django.contrib.staticfiles.templatetags.staticfiles import static
def index(request):
context = {'image_src': static("image.jpg")}
template = loader.get_template('index.html')
return HttpResponse(template.render(context, request))
index.html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML File</title>
</head>
<body>
<img src="{{ image_src }}">
</body>
</html>
UPDATE: Multiple Images
You can generate markup with multiple images and pass it in the context as seen in the views.py:
views.py
from django.contrib.staticfiles.templatetags.staticfiles import static
def index(request):
images = [static('image1.jpg'), static('image2.jpg'), static('image3.jpg')]
images_html = "".join([
"<img src={image}>".format(image=image)
for image in images
])
context = {'images_html': images_html)}
template = loader.get_template('index.html')
return HttpResponse(template.render(context, request))
Now, your updated index.html will be:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML File</title>
</head>
<body>
{{ images_html|safe }}
</body>
</html>
Hope it helps.
working code:
def index(request):
context = {'image_insert': "image.jpg"}
template = loader.get_template('index.html')
return HttpResponse(template.render(context, request))
index.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML File</title>
</head>
<body>
First Image
<img src="{% static "image.jpg" %}">
Second Image <!-- does not work -->
<img src="{% static image_insert %}">
</body>
</html>

flask two for loops encode unicode to utf-8

I'm newbie in flask,I want to get mysql data to show in web.But it's unicode,so I try use two for to solve it .But now I want to know the reason why I can use two for loops to encode unicode to utf-8
flask code
#coding=utf-8
from flask import Flask, request, render_template
from flaskext.mysql import MySQL
mysql = MySQL()
app = Flask(__name__)
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = ''
app.config['MYSQL_DATABASE_DB'] = 'test'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)
#app.route('/')
def showbookname():
cursor = mysql.get_db().cursor()
cursor.execute("SELECT * FROM manager ")
bookname = cursor.fetchall()
return render_template('book.html', bookname = bookname)
if __name__ == '__main__':
app.run( debug = True)
the incorrect HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ bookname }}
</body>
</html>
the result((u'Tom',123'), (u'Mark', u'123')),my expected result is ((Tom,123),(Mark,123))
the correct HTML(although I didn't achieve my goal.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for items in bookname %}
{% for item in items %}
{{ item }}
{% endfor %}
{% endfor %}
</body>
</html>
the reasult is Tom 123 Mark 123 .It's like two for loops iterate over the table and print all data
Your expectation is incorrect unfortunately. The original output is correct - you're printing out the list of rows itself and it's not an unicode issue. That means the rows have to be serialised in some way that's possible to display. In this case that's ((column, column, ...), (column, column, ...), ...). It comes from python's repr(bookname) and you shouldn't rely on it to look in any specific way. It just happens to show which strings are unicode by prepending the u.
If you want the output you expected, you have to construct it yourself:
(
{% for items in bookname %}
(
{% for item in items %}
{{ item }}
{% if not loop.last %}
,
{% endif %}
{% endfor %}
)
{% if not loop.last %}
,
{% endif %}
{% endfor %}
)
You'll have to remove the whitespace from the template if you don't want it in the output. But I expect that in reality you want a different output format - you just need to adjust your template for it.

In jinja2, how to include the same template twice but pass in different variables

In jinja2, I am trying to dynamically create a html document using the template more than once.
My python script looks like this:
# In my python script
env = Environment()
env.loader = FileSystemLoader('.')
base_template = env.get_template('base_template.html')
# each has the actual content and its associated template
content1 = ("Hello World", 'content_template.html')
content2 = ("Foo bar", 'content_template.html')
html_to_present = [content1[1], content2[1]]
# and render. I know this is wrong
# as I am not passing the actual content,
# but this is the part I am struggling with. More below
base_template.render(include_these=html_to_present, ).encode("utf-8"))
And my base template looks like this:
#################
# base_template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
{% for include_this in include_these %}
{% include include_this %}
{% endfor %}
</body>
</html>
and content_template.html looks like this
# content_template.html
<div>
{{ content }}
</div>
Now my question is how do you dynamically set content variable in the content_template.html based on the value associated with it?
Use Jinja2 macros to parameterise your templates.
A macro is like a Python function; you define a template snippet, plus the arguments it takes, and then you can call the macro just like you would a function.
I'd put the macros together into one macro template and import that template into your base template. Pass in the names of the macros you want to use to the base template:
# content and macro name
content1 = ("Hello World", 'content_template')
content2 = ("Foo bar", 'content_template')
base_template.render(include_these=[content1, content2]).encode("utf-8"))
This adds the macro context filter to the environment as well.
and in your base_template.html have:
{% import "macros.html" as macros %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
{% for content, macroname in include_these %}
{% macros[macroname](content) %}
{% endfor %}
</body>
</html>
and the macros.html template:
{% macro content_template(content) -%}
<div>
{{ content }}
</div>
{%- endmacro %}

Categories

Resources