Flask: Difference between revisions

From XPUB & Lens-Based wiki
No edit summary
 
(24 intermediate revisions by 3 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 38: Line 43:
</pre>
</pre>


You can also use variable routes
You can also use variable routes (example for int)
<pre>
<pre>
@app.route("/book/<int:id>")
@app.route("/book/<int:id>")
Line 44: Line 49:
</pre>
</pre>
as you can see you can grab the variable in the url through the function’s parameter
as you can see you can grab the variable in the url through the function’s parameter
(example for string)
<pre>
@app.route("/book/<bookname>")
def book(bookname):
</pre>


== Http Methods ==
== Http Methods ==
Line 61: 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 77: 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 96: Line 104:
     return render_template('404.html'), 404
     return render_template('404.html'), 404
</pre>
</pre>
== 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/
{{ :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 12: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

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