Flask: unable to serve rendered pages from specific directory - python

I have a bunch of files in a directory that I wish to render and serve to the user, but have been unable to. Going to the path always returns just the 'page_template.htm' and not the rendered file. Here's my code:
from flask import Flask, request, render_template, send_from_directory
# Instantiate the Flask app
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
#app.route("/")
#app.route("/index")
def index():
return render_template('index.html')
#app.route("/Special_Data/<path:path>")
def page(path):
page = send_from_directory('Special_Data', path)
return render_template('page_template.htm', page=page)
What I wish to do is to grab raw text files from the 'Special_Data' directory and to render them into html files so they look nice, then send them to the user if they click on a link.
The files are in the directory 'Special_Data' and the 'page_template.htm' is in the 'templates' directory.
Where am I going wrong?

The following example shows you how you can use FlatPages to list, display and offer files for download. The markdown code is rendered beforehand and integrated into the specified or the default template.
from flask import Flask
from flask import render_template, send_file
from flask_flatpages import FlatPages
from io import BytesIO
import os
app = Flask(__name__)
# Optional configuration here!
pages = FlatPages(app)
# ...
# List all available pages.
#app.route('/contents')
def contents():
return render_template('contents.html', pages=pages)
# Display the rendered result.
#app.route('/page/<path:path>')
def page(path):
page = pages.get_or_404(path)
template = page.meta.get('template', 'flatpage.html')
return render_template(template, page=page)
# Download the rendered result.
#app.route('/download/<path:path>')
def download(path):
page = pages.get_or_404(path)
template = page.meta.get('template', 'flatpage.html')
return send_file(
BytesIO(str(render_template(template, page=page)).encode()),
as_attachment=True,
attachment_filename=f'{os.path.basename(path)}.html'
)
templates/contents.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<ul>
{% for page in pages %}
<li>
[Download] -
{{ page.title }}
</li>
{% endfor %}
</ul>
</body>
</html>
templates/flatpage.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ page.title }}</title>
</head>
<body>
{{ page }}
</body>
</html>
pages/Special_Data/ncs1.html
title: Hello
published: 2010-12-22
Hello, *World*!
Lorem ipsum dolor sit amet
If you want to use a different template for rendering, you can define one within the metadata based on the file name. Add the following below published and a suitable template within the templates folder.
template: mytemplate.html

The problem is, that send_from_directory sends the file to the client, it does not load it into memory. So your page variable is actually of type Response instead of str or a fileobject. If you want to read the file and than display it use
import werkzeug
...
#app.route("/Special_Data/<path:path>")
def page(path):
p = werkzeug.security.safe_join("Special_Data", path)
with open(p, "r", encoding="utf-8") as f:
return render_template('page_template.htm', page=f.read())
Otherwise I'm afraid I can't help as I don't know what you want to do.

Using send_from_directory Return a file to client.
So, you need to change like this
#app.route("/Special_Data/<path:path>")
def page(path):
return send_from_directory('Special_Data', path)
Or
return send_file(filename_or_fp=file_path, as_attachment=True, add_etags=True, conditional=True)
render_template will render your html template, it may return a html string. Therefore you need return file instead HTML string

Related

Python variable to html template

In Python I would like to render a python variable "myVariable" - which is already in my workspace - to an html template with flask. I just want to render one single html page to copy paste the source code and publish that on a webserver elsewhere. My code renders local variables but I cannot find a solution for passing workspace variables to the render function.
My template (home.html):
<html>
<head>
<title>Some title</title>
</head>
<body>
<p>My variable = {{ myVariable }}. </p>
</body>
</html>
My python code:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def somfun():
#myVariable = 12345 #**working... but how to use already existing workspace variable?**
return render_template("home.html", myVariable=myVariable)
if __name__ == '__main__':
app.run()

Flask Cannot Display Uploaded Images

I've been following multiple Flask tutorials (but mostly this one) on creating a web app that I can use to read an image into a FastAi model. At some point, I was even able to get the images to upload into a folder, but I could never get them to display on the webpage itself no matter what I tried to do (refactor the function, move the folder to 'static', explicitly declare paths, etc.). I assume I'm probably missing something incredibly basic but I don't know enough about Flask and how web apps work to know what that is.
Edit: I keep getting 400 Bad Request pages every time I attempt to upload an image through the "index.html" page and the image doesn't appear in the folder when I inspect it with Windows Explorer.
file structure:
main.py
app
--> __init__.py
--> routes.py
main.py:
from app import app
main.py:
from flask import Flask
app = Flask(__name__)
from app import routes
routes.py:
from app import app
import os
from fastai.vision.widgets import *
from flask import Flask, render_template, request
#app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
def get_predictions(img_path):
global learner_inf
learner_inf = load_learner('app/static/model/export.pkl')
return learn_inf.predict(img_path)
#app.route('/predict', methods=['GET','POST'])
def predict():
if request.method == 'POST':
file = request.files['file']
filename = file.filename
file_path = os.path.join('app/static/uploads', filename)
file.save(file_path)
result = get_predictions(file_path)
return render_template('predict.html', result = str(result), user_image = file_path)
index.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<form method="POST" action="/predict" enctype="multipart/form-data">
<p><input type="file" name="file /"></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
predict.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<img src="{{ user_image }}" alt="Selected Image" class="img-thumbnail">
<h2>{{ result }}</h2>
</body>
</html>
When referencing static items such as images and files, use the static folder to hold them.
app = Flask(__name__, static_folder="static")
After that, include it in CSS/HTML. This is not the direct application of inserting images in your situation, but it serves as a good example.
background-image: url("bg.gif");
The url to reference the image can be literally anything, just be sure to remember it for later reference. The reference "bg.gif" seen in the CSS automatically does GET request to "yoururl.com/bg.gif" to obtain the image.
Now link that url to the site path.
#app.route('/bg.gif', methods=['GET'])
def bghome():
return send_from_directory("static", "bg.gif")
The route MUST be correct and exact and the file MUST exist. If you use a kwarg to get the image url, be sure to keep that url consistent to what you define in your flask backend pathing.

How to update a template_string on app creation?

Say we use Flask-User to show basic login\logout\edit user page\main page content. Yet their base.html is not perfect (static app name for example embeded into base.html). Say we can only have one python script file for our application (dependencies- yes, additional .html template files-no). How to edit a Flask template_string (for base.html) directly from python code?
One possibility is to create a custom FileSystemLoader class and modify the appropriate template contents returned from its get_source method.
A simple example:
base.html This is the template we want to modify. Suppose we don't like the contents of the title tag.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title set in base template</title>
</head>
<body>
{% block content %}
{% endblock content %}
</body>
</html>
index.html This is our template that extends base.html.
{% extends 'base.html' %}
{% block content %}
<h1>Home Page</h1>
{% endblock content %}
app.py Our simple Flask app.
import os
from flask import Flask, render_template
from jinja2 import FileSystemLoader
class CustomFileSystemLoader(FileSystemLoader):
def __init__(self, searchpath, encoding='utf-8', followlinks=False):
super(CustomFileSystemLoader, self).__init__(searchpath, encoding, followlinks)
def get_source(self, environment, template):
# call the base get_source
contents, filename, uptodate = super(CustomFileSystemLoader, self).get_source(environment, template)
if template == 'base.html':
print contents
# Modify contents here - it's a unicode string
contents = contents.replace(u'Title set in base template', u'My new title')
print contents
return contents, filename, uptodate
app = Flask(__name__)
app.jinja_loader = CustomFileSystemLoader(os.path.join(app.root_path, app.template_folder))
#app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run()
Run the app and notice the title change in the browser.

display image attach from couchDB in html via flask/python

I'm tying to get an image attachment from couchdb using flask & python then pass the image to imgurl.html to be displayed.
The problem is that I'm only get this:
couchdb.http.ResponseBody object at 0x103b9c0b8> returned.
app.py
from flask import Flask, request, render_template, flash, request, url_for, redirect
import couchdb
import requests
app = Flask(__name__)
couch = couchdb.Server('http://127.0.0.1:5984/')
db = couch['test2']
doc = db.get_attachment('2', 'aboutme.jpg', default=None)
print("doc: ", doc)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/imgurl/')
def imgurl():
return render_template('imgurl.html', doc = doc)
#app.route('/page/')
def page():
return("Andrew Irwin")
#return render_template('index.html')
if __name__ == '__main__':
app.run()
test.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="{{ doc }}" id="imgslot" >
<h1 id="h1id"> {{ doc }} </h1>
</body>
</html>
One option could be to base64 encode the image data returned from couchdb and pass the encoded data as a string to the template where you can render it. E.g.
img = db.get_attachment('2', 'aboutme.jpg', default=None).read()
img_b64 = base64.b64encode(img)
def imgurl():
return render_template('imgurl.html', doc = img_b64)
Then in the template:
<img src="data:image/png;base64,{{ doc }}" id="imgslot" >
Another option depending on your security model could be to serve the image directly from couchdb by adding the image url to the image tag, e.g. Getting url for an attachment
Before I saw #SHC's answer I managed to come up with a solution my self.
What I did was retrieve the name of the attachment and then append that to the end of a url like i.e "http:localhost:5984/dbName/docId/attachmentName".
I passed that url to the html img src and it worked then. Thank you you #SHC for your answer. sorry I didnt see it sooner.

Mix images with Markdown in a Flask app

I am building a static site using Flask-FlatPages (and following up with Frozen-Flask).
Within my pages, I want to mix up the text with images. This would be the naive way to do this:
## Look at *this* image:
<img src="{{ url_for('static', filename='images/image.png') }}">
Hmm, it does **not** seem to load.
The {{ template tag }} is not being parsed, because FlatPages runs the page through markdown and not through Flask's templating system (if I am not mistaken).
How do I go about getting the correct image link?
Relevant code
#app.py
from flask import Flask, render_template
from flask_flatpages import FlatPages
app = Flask(__name__)
pages = FlatPages(app)
#app.route('/tip/<path:path>')
def tip_detail(path):
tip = pages.get_or_404(path)
template = tip.meta.get('template', 'tip_detail.html')
return render_template(template, tip=tip)
and
#tip_detail.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>{{ tip.meta.title }}</h1>
{{ tip }}
</body>
</html>
Solved through a comment left by https://github.com/naringas at https://github.com/SimonSapin/Flask-FlatPages/pull/1
As it turns out Flask-FlatPages does not render Jinja template tags. It does however have an option to set a custom HTML renderer. Use that to first render the Jinja template before rendering the markdown.
#add these lines to app.py
def prerender_jinja(text):
prerendered_body = render_template_string(Markup(text))
return pygmented_markdown(prerendered_body)
app.config['FLATPAGES_HTML_RENDERER'] = prerender_jinja

Categories

Resources