Flask: Difference between revisions
(17 intermediate revisions by 2 users not shown) | |||
Line 26: | Line 26: | ||
* the function definition after is mandetory as well as the return. | * the function definition after is mandetory as well as the return. | ||
* everything that comes after return gets sent back to the browser (http GET request) | * everything that comes after return gets sent back to the browser (http GET request) | ||
Save the code above as <code>hello.py</code>. | |||
Run it with: | Run it with: | ||
$ | $ flask --app hello run | ||
And if you want to enable the debugger (recommended!): | |||
$ flask --app hello run --debug | |||
== Paths == | == Paths == | ||
Line 67: | Line 72: | ||
answer = "get" | answer = "get" | ||
if request.method == 'POST': | if request.method == 'POST': | ||
answer = " | answer = "post" | ||
return answer | return answer | ||
</pre> | </pre> | ||
Line 83: | Line 88: | ||
</pre> | </pre> | ||
You can pass as many variables to a template and keep the code separate from the HTML it generates. In this example we pass message to this template: | |||
<pre> | <pre> | ||
<p>{{ message }}</p> | |||
<p>{{message}}</p | |||
</pre> | </pre> | ||
'''!important:''' The | '''!important:''' The template file needs to be saved inside a templates folder called "templates" inside your project folder, this is a Flask default. | ||
== Helpful function == | == Helpful function == | ||
Line 115: | Line 117: | ||
https://hackersandslackers.dev/deploy-flask-uwsgi-nginx/ | https://hackersandslackers.dev/deploy-flask-uwsgi-nginx/ | ||
=== | {{ :Service files }} | ||
==Boilerplates== | |||
You can use these examples as boilerplates for your Flask application! | |||
# Install Flask | |||
# Copy a Flask boilerplate and save it as a Python script (<code>filename.py</code>) somewhere | |||
# Run the Flask application with: <code>flask --app filename run --debug</code> | |||
# Open the Flask application in your browser at: http://localhost:5000 (5000 is the default port Flask uses) | |||
<syntaxhighlight lang="python"> | |||
from flask import Flask, request | |||
app = Flask(__name__) | |||
html_template = """ | |||
<h1>transformations</h1> | |||
<form action="/" method="post"> | |||
<input type="text" name="search"> | |||
<br><br> | |||
<input type="submit" value="transform"> | |||
</form> | |||
""" | |||
@app.route("/", methods=["POST","GET"]) | |||
def transformations(): | |||
if request.method == "POST": | |||
search = request.form["search"] | |||
result_list = [] | |||
for character in search: | |||
unicode_point = format(ord(character)) | |||
result_list.append(character + " " + unicode_point) | |||
result_string = "<br>\n".join(result_list) | |||
return html_template + f"<pre>{ result_string }</pre>" | |||
if request.method == "GET": | |||
return html_template | |||
</syntaxhighlight> | |||
==Running Flask applications on the collective hub.xpub.nl servers== | |||
===gunicorn=== | |||
First install gunicorn and python-dotenv: | |||
$ pip3 install gunicorn python-dotenv | |||
And then make or change the following files: | |||
'''Makefile''' | |||
<pre> | |||
include .env | |||
export | |||
default: local | |||
local: | |||
@flask run --debug | |||
breadcube: | |||
@SCRIPT_NAME=${APPLICATION_ROOT} gunicorn -b localhost:${PORTNUMBER} --reload app:app | |||
</ | </pre> | ||
'''.env''' | |||
<pre> | |||
APPLICATION_ROOT=/breadcube/overlap | |||
PORTNUMBER=5000 | |||
</pre> | |||
'''settings.py''' | |||
<pre> | |||
import os | |||
from dotenv import main | |||
# Load environment variables from the .env file | |||
main.load_dotenv() | |||
''' | # Bind them to Python variables | ||
APPLICATION_ROOT = os.environ.get('APPLICATION_ROOT', '/') | |||
PORTNUMBER = int(os.environ.get('PORTNUMBER', 5000)) | |||
</pre> | |||
'''app.py''' | |||
<pre> | |||
# load the settings of the applicaiton | |||
# (to handle the routing correctly) | |||
app.config.from_pyfile('settings.py') | |||
</pre> | |||
'''nginx''' | |||
<pre> | |||
# overlap flask application | |||
location ^~ /overlap/static/ { | |||
alias /var/www/html/breadbrick/SI21/week5/static/; | |||
autoindex on; | |||
} | |||
location ^~ /overlap/ { | |||
proxy_pass http://localhost:5000/breadcube/overlap/; | |||
} | |||
</pre> | |||
===Kamo's way=== | |||
See: https://git.xpub.nl/kamo/pad-bis#nginx-configuration |
Latest revision as of 11:28, 7 June 2023
Flask
Short introduction guide on Flask. http://flask.pocoo.org/ (Used for XPPL, so to see a more advanced use in connection with database, see XPPL)
Basic Flask
Flask is a microframework for python to create web applications. It basically connects the webserver with your python code.
Install
$ pip install Flask
Simple text serving
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!"
- with @app.route you can define the url flask respons to.
- the function definition after is mandetory as well as the return.
- everything that comes after return gets sent back to the browser (http GET request)
Save the code above as hello.py
.
Run it with:
$ flask --app hello run
And if you want to enable the debugger (recommended!):
$ flask --app hello run --debug
Paths
You can use any route you like
@app.route("/any/route/you/like")
You can also use variable routes (example for int)
@app.route("/book/<int:id>") def book(id):
as you can see you can grab the variable in the url through the function’s parameter
(example for string)
@app.route("/book/<bookname>") def book(bookname):
Http Methods
Methods like GET or POST (DELETE, PUT…) can be handled by flask
Therefore you need to add the wanted methods to the route definition like:
from flask import Flask app = Flask(__name__) @app.route('/address_to_post', methods= ['POST','GET']) def respond_to_post(): answer = "" if request.method == 'GET': answer = "get" if request.method == 'POST': answer = "post" return answer
with request.method you can determine the incoming kind of request.
Templates
To be able return full html pages, flask uses templates using Jinja to insert variable content.
@app.route('/') def home(): message = "Welcome Home!" return render_template('home.html', message=message)
You can pass as many variables to a template and keep the code separate from the HTML it generates. In this example we pass message to this template:
<p>{{ message }}</p>
!important: The template file needs to be saved inside a templates folder called "templates" inside your project folder, this is a Flask default.
Helpful function
404 Page not found
@app.errorhandler(404) def page_not_found(error): """Custom 404 page.""" return render_template('404.html'), 404
Deploying
Running with uwsgi
Flask warns you that using "the development server" isn't good for "production use".
Using software like uwsgi to run your flask project on a server is a good idea. It:
- Is able to deal with many users at the same time
- When configured right lets large static files (like images / videos) be served in a way that works well
https://hackersandslackers.dev/deploy-flask-uwsgi-nginx/
Making a systemd service file
See: https://blog.miguelgrinberg.com/post/running-a-flask-application-as-a-service-with-systemd
Useful: https://containersolutions.github.io/runbooks/posts/linux/debug-systemd-service-units/
$ sudo nano /etc/systemd/system/myflaskapp.service
[Unit] Description=<a description of your application> After=network.target [Service] User=<username> WorkingDirectory=<path to your app> ExecStart=<app start command> Restart=always [Install] WantedBy=multi-user.target
When the service file is new or changed, you need (one time) to:
$ sudo systemctl daemon-reload
Then you can:
$ sudo systemctl start myflaskapp $ sudo systemctl status myflaskapp $ sudo systemctl restart myflaskapp $ sudo systemctl stop myflaskapp
Then finally when you see that start works (checking status, checking that it actually is running , etc)
$ sudo systemctl enable myflaskapp
Will make the "service" auto start when the pi restarts.
To view the log file (errors):
$ sudo journalctl -u myflaskapp -f
You can find documentation here:
https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
Boilerplates
You can use these examples as boilerplates for your Flask application!
- Install Flask
- Copy a Flask boilerplate and save it as a Python script (
filename.py
) somewhere - Run the Flask application with:
flask --app filename run --debug
- Open the Flask application in your browser at: http://localhost:5000 (5000 is the default port Flask uses)
from flask import Flask, request
app = Flask(__name__)
html_template = """
<h1>transformations</h1>
<form action="/" method="post">
<input type="text" name="search">
<br><br>
<input type="submit" value="transform">
</form>
"""
@app.route("/", methods=["POST","GET"])
def transformations():
if request.method == "POST":
search = request.form["search"]
result_list = []
for character in search:
unicode_point = format(ord(character))
result_list.append(character + " " + unicode_point)
result_string = "<br>\n".join(result_list)
return html_template + f"<pre>{ result_string }</pre>"
if request.method == "GET":
return html_template
Running Flask applications on the collective hub.xpub.nl servers
gunicorn
First install gunicorn and python-dotenv:
$ pip3 install gunicorn python-dotenv
And then make or change the following files:
Makefile
include .env export default: local local: @flask run --debug breadcube: @SCRIPT_NAME=${APPLICATION_ROOT} gunicorn -b localhost:${PORTNUMBER} --reload app:app
.env
APPLICATION_ROOT=/breadcube/overlap PORTNUMBER=5000
settings.py
import os from dotenv import main # Load environment variables from the .env file main.load_dotenv() # Bind them to Python variables APPLICATION_ROOT = os.environ.get('APPLICATION_ROOT', '/') PORTNUMBER = int(os.environ.get('PORTNUMBER', 5000))
app.py
# load the settings of the applicaiton # (to handle the routing correctly) app.config.from_pyfile('settings.py')
nginx
# overlap flask application location ^~ /overlap/static/ { alias /var/www/html/breadbrick/SI21/week5/static/; autoindex on; } location ^~ /overlap/ { proxy_pass http://localhost:5000/breadcube/overlap/; }