Flask: Difference between revisions

From XPUB & Lens-Based wiki
 
(20 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:
<pre>
 
$ FLASK_APP=hello.py flask run
$ flask --app hello run
</pre>
 
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 = "get"
         answer = "post"
     return answer
     return answer
</pre>
</pre>
Line 83: Line 88:
</pre>
</pre>


you can pass as many variables to the template as you want. in this example we pass message to the template:
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:


The html looks something like:
<pre>
<pre>
<html>
<p>{{ message }}</p>
<p>{{message}}</p>
<html>
</pre>
</pre>


'''!important:''' The html file needs to be saved inside a templates folder called "templates" inside your project folder-
'''!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 107: Line 109:
=== Running with uwsgi ===
=== Running with uwsgi ===


TODO
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/
 
{{ :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__)


=== Making the systemd service file ===
html_template = """
<h1>transformations</h1>
<form action="/" method="post">
<input type="text" name="search">
<br><br>
<input type="submit" value="transform">
</form>
"""


See: https://blog.miguelgrinberg.com/post/running-a-flask-application-as-a-service-with-systemd
@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)


  sudo nano /etc/systemd/system/myflaskapp.service
        result_string = "<br>\n".join(result_list)


<source>
        return html_template + f"<pre>{ result_string }</pre>"
[Unit]
   
Description=<a description of your application>
    if request.method == "GET":
After=network.target
   
        return html_template
</syntaxhighlight>


[Service]
==Running Flask applications on the collective hub.xpub.nl servers==
User=<username>
WorkingDirectory=<path to your app>
ExecStart=<app start command>
Restart=always


[Install]
===gunicorn===
WantedBy=multi-user.target
</source>


First install gunicorn and python-dotenv:


When the service file is new or changed, you need (one time) to:
$ pip3 install gunicorn python-dotenv


    sudo systemctl daemon-reload
And then make or change the following files:


Then you can:
'''Makefile'''


    sudo systemctl status myflaskapp
<pre>
    sudo systemctl restart myflaskapp
include .env
    sudo systemctl stop myflaskapp
export


'''Then finally''' when you see that start works (checking status, checking that it actually is running , etc)
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>


    sudo systemctl enable myflaskapp
===Kamo's way===


Will make the "service" auto start when the pi restarts.
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!

  1. Install Flask
  2. Copy a Flask boilerplate and save it as a Python script (filename.py) somewhere
  3. Run the Flask application with: flask --app filename run --debug
  4. 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/;
}

Kamo's way

See: https://git.xpub.nl/kamo/pad-bis#nginx-configuration