How to pass items to loop using Jinja2/Flask? - python

I want to pass a list of pages and loop it in Jinja2 to show all pages of my website. I use Flask to construct and run the app. I followed the official flask documentation, together with this tutorial. However when I try to pass the list and try to loop over it, it does not appear in the rendered html.
What am I doing wrong? How to properly pass the list and loop over it using base.html as a template?
Here is my code with a dummy page list hardcoded:
app.py:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
page_list = ['Eins', 'Zwei']
return render_template('base.html', pages=page_list)
if __name__ == "__main__":
app.run(port=8000)
And base.html, located in /templates/:
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>All the nice pages</h1>
{% for page in pages %}
<p>{{ page }}</p>
{% endfor %}
</body>
</html>
When I run the app and browse to http://127.0.0.1:8000/, this is what I get:
<html>
<head>
<title>Test</title>
</head>
<h1>All the nice pages</h1>
<body>
</body>
</html>

This code is totally valid. The important thing is to restart the server if you do changes to lists or dictionaries.
Apart from that, in Flask you can pass any type that is built into Python, be it a list, a dictionary or a tuple.
Here are short example for each of the types that pass more or less the same content:
from flask import Flask, render_template
adictionary = {'spam': 1, 'eggs': 2}
alist = ['Eins', 'Zwei', 'Drei']
atuple = ('spam', 'eggs')
app = Flask(__name__)
#app.route('/')
def index():
return render_template('base.html', pages=alist)
#app.route('/tuple/')
def tuple():
return render_template('base.html', pages=atuple)
#app.route('/dict/')
def adict():
return render_template('base.html', pages=adictionary)
if __name__ == "__main__":
app.run(port=8000)

I was having this same issue. I use Sublime Text 3 and realized that I didn't automatically convert tabs to spaces. Once I made that change in the user settings, and reran the script, it output the list correctly.

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: 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

How to constantly update a variable in flask [duplicate]

This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
Closed 2 years ago.
I want to constantly update my list stuff by appending more "stuff" in it. However, my list is not updating (preferably every second, but I don't know how to do a while loop inside flask).
Here is my routes.py :
from flask import render_template
from app import app
from win32gui import GetWindowText, GetForegroundWindow
#app.route('/')
#app.route('/index')
def index():
user = {'username': 'Miguel'}
stuff = []
stuff.append(GetWindowText(GetForegroundWindow()))
return render_template('index.html', title='Home', user=user, stuff = stuff)
if __name__ == "__main__":
app.run(debug=True)
Here is my index.html :
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog!</title>
{% endif %}
</head>
<body>
<h1>Hello, {{ user.username }}!</h1>
<h1>Here: </h1>
{% for item in stuff %}
<p> {{ item }} </p>
{% endfor %}
</body>
</html>
When I flask run, there is only ever one item in the list. How do I let the program know I want to continue adding more items? I would like to achieve this in the index() function.
Thank you for your help!
Every time your index method is called, the local variable stuff gets re-initialised to an empty list and then you append an element to it. That's why every time you refresh the page, you only see this one newly added element in stuff.
Consider making stuff global and then add items to it:
from flask import render_template
from app import app
from win32gui import GetWindowText, GetForegroundWindow
stuff = []
#app.route('/')
#app.route('/index')
def index():
global stuff
user = {'username': 'Miguel'}
stuff.append(GetWindowText(GetForegroundWindow()))
return render_template('index.html', title='Home', user=user, stuff = stuff)
if __name__ == "__main__":
app.run(debug=True)
Or store global variables in a more better way.

Submitting for python

So I am trying to make a form that accepts text when submitted and returns submitted text using the /process function.
Here is my code for index.html:
<!DOCTYPE>
<html>
<head>
<title>Whats my name</title>
<h1>What's my name?</h1>
</head>
<body>
<input type="text">
<form action="POST"
>
<p>your name</p><input type="submit">
</body>
</html>
And here is my Python code:
from flask import Flask, render_template,redirect # Import Flask to allow us to create our app, and import
# render_template to allow us to render index.html.
app = Flask(__name__) # Global variable __name__ tells Flask whether or not we
# are running the file directly or importing it as a module.
#app.route('/')
def home():
return render_template('index.html')
#app.route('/process',methods=['POST'])
def input():
return redirect ('/')
app.run(debug=True)
To retrieve the name value from your html you'll have to add a tag name to the input.
Please see example below, here I named it user_name:
<html>
{...}
<body>
<form action="" method="post">
<input type="text" name="user_name"/>
<p>your name</p>
<input type="submit"/>
</form>
</body>
</html>
Then request the value in your backend Python code
# import the needed request module from Flask
from flask import request
(...)
#app.route('/process', methods=['POST'])
def input():
name = request.form['user_name']
return name
Check this out first: https://www.w3schools.com/tags/att_form_action.asp
Action should be "/process" instead of "POST".
Method is "POST". Also you will need input elements in the form to allow user inputs something.
The input value can be retrieved on the flask side by request.form['value of name attribute of input']
https://www.w3schools.com/tags/tag_input.asp
I would like to recommend you to use https://flask-wtf.readthedocs.io/en/stable/ flask wtf to generate form and retrieve users' input.

Why is this simple app returning a 500 error?

I am getting an Internal Server error, and I'm not sure what the issue is. index.html is in the same directory as the Python file.
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def hello_world():
author = "Rick"
name = "1st flask app"
return render_template('index.html', author=author, name=name)
if __name__ == "__main__":
app.run()
<!DOCTYPE html>
<html>
<head>
<title>{{ author }}'s app</title>
</head>
<body>
<h2>Hello {{ name }}!</h2>
<p>Please show up on the page</p>
</body>
</html>
Why am I getting a 500 error rather than my rendered template?
Create a directory called templates and put index.html in it.
You can get more information about errors by running in debug mode:
app.run(debug=True)
When you create your app
app = Flask(__name__)
you can also include the template_folder parameter to specify the folder that contains your templates. If you don't, render_template will look for them in the default folder called templates. If such folder doesn´t exist, or if your template can't be found inside it, you will get an internal server error 500 rather than your rendered template.

Categories

Resources