How to update a template_string on app creation? - python

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.

Related

Flask: unable to serve rendered pages from specific directory

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

Having problems with the usage of a block in Flask

so I tried bulding a (small) website (for learning purposes) and had a few problems with the usage of a block.
I have the feeling that the problem is something obvious, but I can't figure it out.
Here is my code:
# main.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def home():
return render_template("main.html")
if __name__ == "__main__":
app.run(debug=True)
# main.html
<!DOCTYPE html>
<head>
<title>
{% block title %}{% endblock %}
</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
# mainexdended.html
{% extends "main.html" %}
{% block title %}Test{% endblock %}
{% block content %}
<h1> Content Test </h1>
{% endblock %}
Now, when I run the code, I just get a blank page. Everything defined in the mainextended.html gets ignored.
How can I change that?
That's because you are rendering main.html, not mainextended.html.
You need to change
#app.route("/")
def home():
return render_template("mainextended.html")
Than you should see the contents of both, main.html and mainextended.html
If you use "extend" you always want to render the "extending" html, there is also a function to include content tho, which would work the other way round.

While using flask in python, I am not able to get the 'extend' feature to work properly

I'm new to programming. I decided to try to create a web app using flask, python, and HTML. I set up three files; two HTML and one python:
This is the HTML file that should have information replaced (didntwork.html):
<!doctype html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<h3>Information:</h3>
{% block body %}{% endblock %}
</body>
</html>
.
This is the file that should input information to the first (connection.html):
{% extends 'didntwork.html' %}
{% block body %}
<h4>information content</h4>
{% endblock %}
.
Finally, this is the logic for the flask app in python (main.py):
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('didntwork.html')
if __name__ == '__main__':
app.run(debug=True)
Unfortunately, the extends in the second HTML file does not seem to be recognized, and the information in its block will not be replaced in the first HTML file (didntwork.html).
This is the output:
The expected outcome should append an h4 tag to the body with the phrase 'information content'.
Any help would be appreciated, thanks.
The problem is the {% extends 'didntwork.html' %}. From what I can tell the file that contains this code:
<!doctype html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<h3>Information:</h3>
{% block body %}{% endblock %}
</body>
</html>
is your connection.html file.
You are extending to the same file you're rendering (didntwork.html). You have to change the extends line to extend your base html, connection.html in this case.
Use this code in your didntwork.html:
{% extends 'connection.html' %}
{% block body %}
<h4>information content</h4>
{% endblock %}
You can find more information in Template Inheritance
UPDATE BASE ON CLARIFICATION
You're rendering the wrong file in your route.
You should render the connection.html and this will extends your didntwork.html file.

How to pass multiple templates to flask.render_template()

I'm trying to deploy a flask app and I want a flask.render_template() method passed with a list of html files. Here I see it's eligible. http://flask.pocoo.org/docs/0.12/api/#flask.render_template
I'm trying with this code
from flask import Flask, render_template
app = Flask(__name__)
app.debug = True
#app.route('/')
def hello():
templs = ["_header.html", "_footer.html"]
return render_template(templs)
if __name__== '__main__':
app.run()
But actually server returns only the first template from the list.
How to iterate though this list in order to render all templates from the list?
Thanks,
Alex
As far as i see you are trying to render static header and footer. I'd recommend to prepare something like "layout.html" with included header and footer:
//layout.html
<html>
<head>//headhere</head>
<header>//your static header</header>
<main>
{% block body %}
//content will be here
{% endblock %}
</main>
<footer> //your static footer </footer>
</html>
then in "child" templates(ex: index.html)use:
//index.html
{% extends "layout.html" %}
{% block body %}
//your code here
{% endblock %}
It will render header and footer from layout.html and rest from index.html.
You probably don't want to render multiple templates. What you want is to render one template that combines multiple templates. That is the task of templating engine, not Flask. See http://jinja.pocoo.org/docs/dev/templates/ (Flask uses Jinja).
Exactly like #jbasko wrote!
Making use of two {{block}} statements worked for me:
For example, I put in my routes.py
#app.route("/page1")
def page1():
return render_template('page1.html', title='Title')
the page1.html is containing simply two block-specifications
{% extends "page1.html" %}
{% block content %}
<h1> Content </h1>
{% endblock content %}
{% block info %}
<h1> Info </h1>
{% endblock info %}
which gets referenced in layout.html in two separate places:
{% block content %}{% endblock %}
and later
{% block info %}{% endblock %}
Thanks for the help everyone!
you can try this...
from flask import Flask, render_template
app = Flask(__name__)
app.debug = True
#app.route('/')
def hello():
render_header = render_template('_header.html')
render_footer = render_template('_footer.html')
return render_header + render_footer
if __name__ == '__main__':
app.run()

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