attach img file in pdf weasyprint - python

I need help with attaching img file in pdfs. We use the WeasyPrint lib for generating pdf from html.
in html connect img file like this
<img src="1.png" alt="">
<img src="2.png" alt="">
<img src="3.png" alt="">
but it is not working. I don't see the image.

use static for path of the image file
{% load static %}
<img src="{% static 'images/static.jpg' %}" alt="">
and pass base_url in HTML class in views.py
pdf_file = HTML(string=rendered_html, base_url=request.build_absolute_uri())
html file
<!DOCTYPE html>
<html lang="en">
{% load static %}
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<img src="{% static 'images/static.jpg' %}" alt="">
</div>
</body>
</html>
views.py
from django.template.loader import get_template
from weasyprint import HTML, CSS
from django.conf import settings
from django.http import HttpResponse
def generate_pdf(request):
html_template = get_template('latest/html_pdf.html')
user = request.user
rendered_html = html_template.render().encode(encoding="UTF-8")
pdf_file = HTML(string=rendered_html, base_url=request.build_absolute_uri()).write_pdf(stylesheets=[CSS(settings.STATIC_ROOT + '/css/generate_html.css')])
http_response = HttpResponse(pdf_file, content_type='application/pdf')
http_response['Content-Disposition'] = 'filename="generate_html.pdf"'
return http_response

In case of sending emails with pdf attached, it's possible to pass path from view to function dedicated to emails.
views.py
[...]
path = request.build_absolute_uri() # build absolute path
order_confirmation.delay(order.id, path) # pass to func
[...]
tasks.py
#app.task
def order_confirmation(order_id, path): # receive path
order = Order.objects.get(id=order_id)
subject = f"Order nr. {order.id}"
email_from = settings.EMAIL
email_to = order.get_email
message = (...)
email = EmailMessage(subject, message, email_from, [email_to])
html = render_to_string('pdf.html', {'order' : order, 'company': company})
out = BytesIO()
stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + '/css/pdf.css')]
weasyprint.HTML(string=html, base_url=path).write_pdf(out, stylesheets=stylesheets) # use here
email.attach(f'order_{order.id}.pdf',
out.getvalue(),
'application/pdf')
email.send()

Related

Is there a way to parse a file through to a <source> tag in HTML

I'm trying to make a private file host and I have it set to return a send_file() when the, for example, /media/someSong.mp3 url is called.
But I want to make it more styled so when you visit /media/someSong.mp3, instead of your browser trying to play that MP3 in it, it will use a HTML page with a tag that parses the send_file() sent by an argument.
Currently, I have this:
#app.route('/media/<mediaID>')
def mediaStuff(mediaID):
try:
return render_template('parse-it.html', title=title, media_file=send_file(f'./media/{mediaID}'))
# return send_file(f'./media/{mediaID}')
except FileNotFoundError:
return 'The media you tried to view doesn\'t exist.'
And here is what I have in my templates/parse-it.html file:
{% extends "base.html" %}
{% block content %}
<h1>You are now viewing ...!</h1>
<source src={{media_file}}>
{% endblock %}
However all I see is <Response 200 OK> or something like that. No file to be seen!
Would I use a get_file() and then return Response(getfileVar) in that media_file argument?
send_file returns a Response object. It makes no sense to pass this to the template.
So you need two endpoints. One to display your website containing the media element and one to stream the file from the directory. I recommend using send_from_directory for this.
This is a simple example that lists all files within a directory and offers them for playback or viewing. The distinction is made based on the mime type of the file. This is determined via guess_type.
The exmpale uses the instance path to ensure separation of files and application.
import os
from flask import (
Flask,
render_template,
send_from_directory
)
from mimetypes import guess_type
app = Flask(__name__)
app.config['MEDIA_FOLDER'] = os.path.join(app.instance_path, 'media')
try:
os.makedirs(app.config['MEDIA_FOLDER'])
except:
pass
def listfiles(target):
for filename in os.listdir(target):
mimetype = guess_type(os.path.join(target, filename))
yield (
filename,
mimetype,
)
#app.route('/')
def index():
files = listfiles(app.config['MEDIA_FOLDER'])
return render_template('index.html', **locals())
#app.route('/media/<path:filename>')
def media(filename):
return send_from_directory(
app.config['MEDIA_FOLDER'],
filename
)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
{% for filename, type in files -%}
<div>
{% set mimetype, encoding = type -%}
{% if mimetype.startswith('audio') -%}
<audio controls>
<source src="{{ url_for('media', filename=filename) }}" type="{{ mimetype }}">
</audio>
{% elif mimetype.startswith('image') -%}
<img src="{{ url_for('media', filename=filename) }}" />
{% endif -%}
</div>
{% endfor -%}
</body>
</html>

Loading images from an AWS bucket media folder to a HTML pdf template

I am trying to generate a PDF file out of a post created by a user, and include an image, which I want to store in an AWS bucket.
This is how it was working with my own file system
My function to generate a pdf in views.py:
def form_render_pdf_view(request, *args, **kwargs):
pk = kwargs.get('pk')
form = get_object_or_404(Post, pk=pk)
template_path = 'form/pdf2.html'
context = {
'form': form
}
# Create a Django response object, and specify content_type as pdf
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename="report.pdf"'
# find the template and render it.
template = get_template(template_path)
html = template.render(context)
# create a pdf
pisa_status = pisa.CreatePDF(
html, dest=response)
if pisa_status.err:
return HttpResponse('We had some errors <pre>' + html + '</pre>')
return response
And rendering the image in the html template
<div>
<img src="{{form.image.path}}">
</div>
Now after uploading the files in a bucket it doesn't seem to work. The image is being stored there after uploading the post, but when creating the PDF the image is not displayed.(And it gives me an error: This backend doesn't support absolute paths). I tried to change the image source in the HTML template but it still didn't work.
Here is how I was able to implement it successfully. Hope it helps
url.py
path('foo/bar/<str:bar_id>/report/pdf/', getPdf,name="bar-report-pdf"),
process.py
from io import BytesIO
from django.conf import settings
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
def html_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
do not use the link_callback attribute of the pisaDocument when using external url
view.py
from foo.process import html_to_pdf
def getPdf(request,bar_id):
image_set = Bar.objects.filter(id=bar_id).all()
context = {
'title':"Inspection Report",
"images": image_set,
}
open('templates/temp.html', "w").write(render_to_string('app/bar-report-pdf.html',context ))
pdf = html_to_pdf('temp.html')
return HttpResponse(pdf, content_type='application/pdf')
bar-report-pdf.html
<!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>Document</title>
</head>
<body>
{% for image in images %}
<img class="d-block img-fluid" src="{{image.thumbnail_url_public}}" alt="{{forloop.counter}}">
{% endfor %}
</body>
</html>
here image.thumbnail_url_public is a text field containing public s3 image url. you can also use the presigned url if its a private s3 bucket.
hope it helps

Data from django form is not being posted in Postgressql Database despite getting 200 OK from the server

I am using Django version 3.0 to create a sign up application for various departments at my school. I have connected the form to a model and the model is stored in a postgressql database. After I start the server using python manage.py runserver and I complete the form and click save, the form refreshes and I get the OK HTTP Code 200 from the server accessed through the terminal, but when I query the postgressql database using pg Admin interface the data has not been stored. Can anyone see where I am going wrong? This process worked before, I'm not sure if the data is just being stored in another table. Please help out if you can.
This is my base.html file:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Signup Prototype</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="{% static 'js/jquery.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
<script src="{% static 'js/bootstrap.js' %}"></script>
<link type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/themes/redmond/jquery-ui.css" rel="Stylesheet" />
<script language="javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/jquery-ui.min.js"></script>
<script type="text/javascript">
$(function(){
$("#id_date").datepicker();
});
</script>
{{form.media}}
</head>
<body>
<header class="page-header">
<div class="container">
<a href="{% url 'Engineering' %}" class="top-menu">
</a>
<h1>Signup </h1>
</div>
</header>
<main class="content container">
<div class="row">
<div class="col">
{% block content %}
{% endblock %}
</div>
</div>
</main>
</body>
</html>
This is my engineering.html file that displays the form
<html>
{% extends 'base.html' %}
{% load static %}
{% block content %}
<h2>Engineering</h2>
<form method="POST" class="engineering-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
<html>
This is my .views file:
from django.shortcuts import render
from .forms import Engineering
def engineering(request):
form = Engineering()
return render(request, 'my_app/engineering.html', {'form': form})
This is my form
class Engineering(forms.ModelForm):
name = forms.CharField(label="Name")
etoken_user = forms.BooleanField(required = False, label="Etoken Use")
mfa_use = forms.BooleanField(required = False, label="MFA Use")
usage_type = forms.CharField(label="Usage Type")
email = forms.EmailField(label="Email")
user_id = forms.CharField(label="USERid")
date = forms.DateTimeField(label="Date") # date just time stamp the date they are using
class Meta: #tells django which model should be used to create the form
model = Engineering
fields = ('name','etoken_user', 'mfa_use','usage_type','email','user_id','date')
widgets = {
'datetime':DateTimeWidget(attrs={'id':'UTC' },usel10n=True, bootstrap_version=4)
}
this is the model
class Engineering(models.Model):
user_name = models.CharField(max_length = 100)
etoken_use = models.BooleanField()
mfa_use = models.BooleanField()
usage_type = models.CharField(max_length = 100)
date_recorded = models.DateTimeField(auto_now_add = True)
email = models.EmailField(max_length = 150)
user_id = models.CharField(max_length = 20)
As the earlier answer noted, you are not doing anything with the data posted from the forms in the view.
from django.shortcuts import render
from .forms import Engineering
def engineering(request):
form = Engineering()
if request.method == 'POST':
form = Engineering(request.POST)
if form.is_valid():
form.save()
# probably a redirect over here.
return render(request, 'my_app/engineering.html', {'form': form})
yes, because your view does not do anything with the data on POST.
See https://docs.djangoproject.com/en/3.0/topics/forms/#the-view

capture image and save it to database with django

I am trying to make a web app with django in which it clicks an image from the camera and saves the image to a database. How can that be implemented? if there is a source code available, kindly share a link to it. thank you
I came here hoping to find an answer; there not being one, I hereby offer mine ...
The trick is to encode the image and save it as a TextField.
The solution I provide is bit clunky in the way the images are read every time select_image.html is loaded, but I did that because my requirement is a bit more complex and this does the job. I assume you know how to get the image from the camera (which is equivalent to my reading it from file).
There are two templates, select_image.html and display_image.html. When select_image.html is called for the first time it reads .png images from file and saves the images to the database. When an image is selected it is retrieved from the database and displayed in display_image.html.
select_image.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Select Image</title>
</head>
<body>
<h1>Select Image</h1>
{% block content %}
<ul>
{% for image in images %}
<li>{{ image }}</li>
{% endfor %}
</ul>
{% endblock %}
</body>
</html>
display_image.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Display Image</title>
</head>
<body>
<h1>Display Image</h1>
{% block content %}
<img id="image" src="{{ image }}" alt="image">
{% endblock %}
<p></p>
Images
</body>
</html>
models.py
class Image(models.Model):
name = models.CharField(max_length=25, null= False, blank=False, default='image_name', unique=True)
image = models.TextField()
urls.py
urlpatterns = [
path('images', views.Images.as_view(), name='images'),
path('display-image/<str:image_name>/', views.DisplayImage.as_view(), name='display-image'),
]
views.py
from PIL import Image as PilImage
import io
import base64
import os
from .models import Image
class Images(View):
url = "images/select_image.html"
def get(self, request):
dir = <image file directory>
image_names = ['red', 'green', 'blue']
for image_name in image_names:
base_path = os.sep.join([dir, image_name])
path = '.'.join([base_path, 'png'])
pil_image = PilImage.open(path)
encoded_image = self._encode_image(pil_image)
Image.objects.get_or_create(name=image_name, image=encoded_image)
context = {'images': image_names}
return render(request, self.url, context)
#staticmethod
def _encode_image(image):
"""Return image encoded to base64 from a PIL.Image.Image."""
io_buffer = io.BytesIO()
image.save(io_buffer, format='PNG')
saved_image = io_buffer.getvalue()
encoded_image = ''.join(['data:image/jpg;base64,', base64.b64encode(saved_image).decode()])
return encoded_image
class DisplayImage(View):
url = "images/display_image.html"
def get(self, request, image_name):
image = get_object_or_404(Image, name=image_name)
context = {'image': image.image}
return render(request, self.url, context)

Jinja2 Using images in HTML

I wasted a lot of time to find out what is wrong. I need your help now.
I want to render template with image from my filesystem. But it is not working.
Path - string that contains file name
#app.route('/', methods=['GET'])
def main():
return render_template('main.html',image = path)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<h2></h2>
<form method="post" enctype="multipart/form-data" action="/uploader">
<input type="file" name="file">
<button type="submit">Upload</button>
</form>
<img src={{ url_for('static', filename = image) }} >//i can't figure how to change this to use {{image}}
</body>
</html>
Just generate full image path in your view and pass it to your template
#app.route('/', methods=['GET'])
def main():
#path is filename string
image_file = url_for('static', filename=path)
return render_template('main.html', image_file=image_file)
and then just use it as full link
<img src={{ image_file}} >
if you have image file (with filename which stored in path) in your static folder this should work
If you was set the static folder like that (or other methods):
from flask import Flask
app = Flask(__name__, static_folder='static')
#app.route('/', methods=['GET'])
def main():
return render_template('main.html', image = url_for("static", filename=path))
or shorter:
return render_template('main.html', image='/static/' + path))
after you will put your image in /static folder you can get it from template:
<img src="{{image)}}">
I think something like this should work.
<img src="{{ url_for('static', filename='image') }}">
Where 'image' refers to the path of the image file inside static folder. Make sure to use the full name of image file along with its file type i.e. jpg, png etc.

Categories