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
Related
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
This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 9 months ago.
I am using Flask render_template method to render my html page as below:
render_template('index.html', content="<div>Hello World!</div>")
My index.html is:
<html lang="en">
<head>
<title>Hello</title>
</head>
<body>
{{content}}
</body>
</html>
My page is replacing the content variable, but it is rendering <div>Hello World!</div> as text.
Is there a way to render html from render_template context variables?
Flask turns on Jinja's autoescape feature. Quoting the manual on how to disable it:
There are three ways to accomplish that:
In the Python code, wrap the HTML string in a Markup object before passing it to the template. This is in general the recommended way.
Inside the template, use the |safe filter to explicitly mark a string as safe HTML ({{ myvariable|safe }})`
Temporarily disable the autoescape system altogether.
If you do not require passing in HTML tags into the template, you may simply do something that looks like this
Template
<html lang="en">
<head>
<title>Hello</title>
</head>
<body>
<div>
{{content}}
</div>
</body>
</html>
Python code
#app.route('/')
def home():
render_template('index.html', content="Hello World!")
This should be perfectly fine. If you do want to add html into your content variable, then the answer by #Ture Pålsson is the best way forward.
Ive made a flask script which runs fine however im trying to display some values in a table on another html page which for some reason is not happening.
i've already tried going through jinja2 documentation and a few other answers but it didn't help much.
the flask file.py
from flask import Flask,render_template,request
app = Flask(__name__)
from webscraper import keeda,display_tbl
#app.route('/', methods=['POST', 'GET'])
def scraper():
if request.method == 'POST':
url = request.form['url']
df=keeda(url)
return render_template(('completed.html',display_tbl(df)))
else:
return render_template('index.html')
if __name__ == '__main__':
app.run()
the completed.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Summary of Scraped Data</title>
</head>
<body>
<h1>This is what you got! </h1>
<div>
{{ display_tbl(df) }}
</div>
</body>
</html>
here's the error: jinja2.exceptions.UndefinedError: 'display_tbl' is undefined
i wanted to display a table with values on this page.
You are expecting more than what jinja2 can do for you. Please understand jinja2 is just a way to render templates which are eventually html and javascript, nothing fancy. So, in your case you cannot pass a Python function to your jinja2 template and expect that to run fine. What you can do here is to pass the data returned by display_tbl while rendering template like this:
def scraper():
...
return render_template(('completed.html', data=display_tbl(df))) # data= is important because this is how you are going to access your data in the template
…
def display_tbl(df):
… # Here you should be returning the data you want to display - a list or dict
In the template
<html>
<head>
<meta charset="UTF-8">
<title>Summary of Scraped Data</title>
</head>
<body>
<h1>This is what you got! </h1>
<div>
{{ render_data() }}
</div>
<script>
var d = data | tojson
function render_data() {
// implement the logic to handle how your data should be rendered
}
</script>
</body>
</html>
This is just a rough idea but as you can see you need to change the way you are perceiving jinja2 templates and their interaction with Python or Flask backend.
view.py
map = folium.Map(location=[df['latitude'].mean(),
df['longitude'].mean()],tiles="cartodbpositron",zoom_start=12)
map.save("map.html")
context = {'my_map': map}
return render(request, 'my_map.html', context)
my_map.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ my_map }}
</body>
browser result:
folium.folium.Map object at 0x7f49d85662b0
im not sure how to approach getting the html/js to work on the browser after the user has submitted their input via the previous html form...
I have seemed to look everywhere and there are a lot of similar problems with solutions but I could not get any to work!
Thanks!
This response is here to increase the google coverage for others who, like me, also experienced this problem when trying to render a Folium map within a Django template.
Your Code
Please see the comments inside each code block for how to render the map as expected.
views.py
map = folium.Map(location=[df['latitude'].mean(),
df['longitude'].mean()],tiles="cartodbpositron",zoom_start=12)
map.save("map.html")
# {'my_map': map} will output the object, which is what you are seeing
# to rectify this we need to turn it into an iframe which
# the template can then render.
context = {'my_map': map} # change to {'my_map': map._repr_html_()}
return render(request, 'my_map.html', context)
Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
# after making the change in our views.py this will return the html but
# the template will not render it as expected because it is being escaped.
# You must declare it 'safe' before it will be rendered correctly.
{{ my_map }} # change to {{ my_map | safe }}
</body>
For more information see the Folium doc page here or this SO post.
Hope that helps.
Map objects have a render method which render its html representation.
You can try directly:
<body>
{{ my_map.render }}
</body>
Or you can use the Map.render method to implement a custom inclusion tag, that way you can pass arguments to the render method. See this for reading more about inclusion and custom tags.
# The template tag.
for django.template import Library
register = Library()
#register.inclusion_tag
def render_map(map_object, **kwargs):
return map_object.render(**kwargs)
In your template:
<body>
{% render_map my_map some_arg1=arg1 some_arg2=arg2 %}
</body>
In this example:
from flask import Flask, render_template, redirect, session
app = Flask(__name__)
app.secret_key="secret"
#app.route('/')
def landing():
session['results']="<p>Test one</p>"
session['results']+="<p>Test two</p>"
results=session['results']
return render_template('index.html', results=results)
app.run(debug='True')
In my html, I have something like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Game</title>
</head>
<body>
{{ results }}
</body>
</html>
The results are an html page that does not interpret the tags. So, I get a page that looks like this:
<p>Test One</p><p>Test Two</p>
You could escape the HTML:
{{ results|safe}}
or in Python
import jinja2
results = jinja2.escape(results)
The framework is escaping the HTML in the results variable to prevent security holes. Ideally you want to keep the HTML in the template and not be passed in via the variables. The best way to achieve what you want is to iterate over the values in results variable and wrap it in p tags in the template. This can be done like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Game</title>
</head>
<body>
{% for result in results %}
<p>{{ result }}</p>
{% endfor %}
</body>
</html>
The templating language is Jinja2, and you can read about that here: http://jinja.pocoo.org/
Try this:
from flask import Markup
results = Markup(results)