Flask

Flask

Moduł flask - tworzenie strony www

Quick start

Instalacja zleżności:

cd 87_flask_imprezka
python -m venv .venv
source .venv/bin/activate
pip install Flask
pip install -r requirements.txt

run main.py on port 8080:

cd src
flask --app main --debug run --host=0.0.0.0 --port=8080

cleanup

pip list
deactivate
pip list
Package      Version
------------ -------
blinker      1.9.0
click        8.1.8
colorama     0.4.6
Flask        3.1.0
itsdangerous 2.2.0
Jinja2       3.1.5
MarkupSafe   3.0.2
pip          24.2
Werkzeug     3.1.3

Setup for dev

Folder structure:

my_flask_app
├── .gitignore
├── requirements.txt
└── src/
    └── main.py

Plik requirements.txt:

Flask
Jinja2
gunicorn

Plik .gitignore:

.venv/

Python

Plik src/main.py:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World!"

if __name__ == '__main__':
    # app.run() # ip=127.0.0.1, port=80
    app.run(host='0.0.0.0', port=8080)    

Uruchomienie (tryb debug oraz reload). Na produkcji użyć Gunicorn.

cd src
flask --app main --debug run --host=0.0.0.0 --port=8080

Uruchomienie (wersja 2):

su
python main.py

Dodaj template

my_flask_app
├── .gitignore
├── requirements.txt
└── src/
    ├── main.py
    └── templates/
        └── main.html

Plik main.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>htmx</title>
</head>
<body>
    <h1>htmx</h1>

</body>
</html>

Plik main.py:

from flask import Flask
from flask import render_template, redirect, send_from_directory

app = Flask(__name__)

@app.route('/hello')
def hello():
    return "Hello World!"

@app.route('/')
def htmx():
    return render_template('main.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

static

Dodaj:

@app.route('/static/<path:path>')
def send_report(path):
    return send_from_directory('static', path)

Dodaj:

<body>
    <h1>htmx</h1>

    <img src="static/dog.png" alt="">
</body>

bootstrap

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

    <title>htmx</title>
</head>

<body>
    <h1>htmx</h1>

    <img src="static/dog.png" alt="">
    
    <button type="button" class="btn btn-primary">Primary</button>
</body>

htmx

Version 1

Plik main.html:

<html lang="en">
    <head>
        <script src="https://unpkg.com/htmx.org@2.0.4"></script>
    </head>
    <body class="m-2">
        <div class="card" style="width: 18rem;">
            <div class="card-body">
                <h5 class="card-title">Text</h5>
                
                <button type="button" class="btn btn-primary"
                    hx-post="/clicked_text" 
                    hx-target="#parent-div"
                    hx-swap="innerHTML" >Primary</button>
                <div id="parent-div">
                    Text1
                </div>
            </div>
        </div>
    </body>
</html>

Plik text.html:

    Text2

Plik main.py:

from flask import Flask
from flask import render_template, redirect, send_from_directory, request

@app.route('/clicked_text', methods = ['POST'])
def clicked_text():
    if request.method == 'POST':
        return render_template('text.html')

Version 2

    <div class="card" style="width: 18rem;">
        <div class="card-body">
            <h5 class="card-title">Counter</h5>

            <button type="button" class="btn btn-primary"
                hx-post="/clicked_button"
                hx-swap="outerHTML"
                hx-vals='{ "userNumber": "1" }'>
                plus 1
            </button>
        </div>
    </div>

Plik button.html:

<button type="button" class="btn btn-primary"
    hx-post="/clicked_button"
    hx-swap="outerHTML"
    hx-vals='{ "userNumber": "{{userNumber}}" }'>
    plus {{userNumber}}
</button>
@app.route('/clicked_button', methods = ['POST'])
def clicked_button():
    if request.method == 'POST':
        userNumber = int(request.form['userNumber'])
        return render_template('button.html', userNumber=userNumber+1)

Serwowanie różnych adresów

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    """http://127.0.0.1:5000"""
    return "Hello World!"

@app.route('/name')
def hello_name():
    """http://127.0.0.1:5000/name"""
    return "Hello World Rafał!"

if __name__ == '__main__':
    app.run() # domyślnie ip=127.0.0.1, port=5000
    #app.run(host='0.0.0.0', port=80)

Uruchomienie:

su
python app.py

Serwowanie z szablonów html, css

Zawartość projektu:

app_web
├── templates
│   ├── main.css
│   └── main.html
└── app.py

./templates/main.css

.button {
    background-color: #5bb5ff; 
    border: 2px solid #5bb5ff;
    color: #002849;
    padding: 16px 32px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
    margin: 4px 2px;
    transition-duration: 0.1s;
    cursor: pointer;
    border-radius: 5px;
    border: none;
    box-shadow: 0 7px #999;
    outline: none;
    border: 2px solid #5bb5ff;
  }
  
  .button:hover {
    background-color: #2ca0ff;
    border: 2px solid #007add;
    color: #000000;
  }
  
  .button:active {
    background-color: #2ca0ff;
    box-shadow: 0 5px #666;
    transform: translateY(2px);
  }

./templates/main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/templates/main.css">
    <title>Ave DocMaker</title>
</head>
<body>
    <h2>Lista plików *.md</h2>
    Pliki *.md do konwersji (cd {{ in_folder }}):
    <ul>
        {% for item in md_files %}
        <li> {{ item }} </li>
        {% endfor %}
    </ul>
</body>
</html>

./app.py

from flask import render_template
from flask import Flask, request, send_from_directory
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    if request.method == "GET":
        return render_template('main.html',
            in_folder = '', 
            md_files = ['file1', 'file2'])

if __name__ == '__main__':
    app.run() # domyślnie ip=127.0.0.1, port=5000
    #app.run(host='0.0.0.0', port=80)

Uruchomienie

Uruchomienie:

su
python app.py

Inny sposób:

export FLASK_APP=app.py # domyślnie app.py
flask run
flask run --host=0.0.0.0  # domyślnie port=5000

Gunicorn:

python -m gunicorn -w 1 -b 0.0.0.0:80 --reload web:app

gunicorn -w 4 -b 0.0.0.0:5000 web:app
gunicorn -w 1 -b 0.0.0.0:5000 --reload web:app

Deploy Linux

cd 87_flask
source .venv/bin/activate
cd src
gunicorn -w 4 -b 0.0.0.0:6060 main:app
killall gunicorn

Docker

Plik Dockerfile:

FROM python:3

RUN apt update
RUN apt install -y gcc

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5001
COPY . .

WORKDIR /app/src

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5001", "app:app"]

Plik restart_docker.sh:

docker stop ave_dyplom
docker rm ave_dyplom

docker build -t ave_dyplom .

docker run --restart=always --name ave_dyplom -p 5001:5001 -d -v "output:/app/pdfs" ave_dyplom

echo 'http://rpi:5001/'

# log:
# docker logs -f --tail 10 ave_dyplom

# bash:
# docker run -it -v "/ave/home/03_apps/01_dyplom/pdfs:/app/pdfs" ave_dyplom  bash
# docker run -it ave_dyplom bash

nginx