Jest to framework, który cenią sobie doświadczeni programiści za minimalizm, przejrzystość i rozszerzalność. W tym przewodniku poznasz podstawy Flask: od instalacji po zaawansowane funkcjonalności, routing, szablony, sesje i integrację z bazą danych.
Instalacja i uruchomienie pierwszej aplikacji
Zacznij od instalacji Flask:
Bash1pip install flask
Utwórz plik app.py
Python1 2 3 4 5 6 7 8 9 10from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Witaj w Flask!" if __name__ == "__main__": app.run(debug=True)
Uruchom aplikację
Bash1flask run
Lub bezpośrednio:
Bash1python app.py
Flask domyślnie wystartuje na http://127.0.0.1:5000.
Konfiguracja trybu debugowania
Python1 2 3 4app = Flask(__name__) if __name__ == "__main__": app.run(debug=True)# Automatyczne przeładowanie przy zmianach
Tryb debugowania włącza:
- Automatyczne przeładowanie przy zmianach w kodzie
- Interaktywny debugger w przeglądarce
- Szczegółowe komunikaty błędów
Routing – obsługa różnych adresów
W Flasku routing to serce aplikacji. Każdy adres URL w Twojej aplikacji jest powiązany z konkretną funkcją (tzw. view function).
Podstawowe routing
Python1 2 3 4 5 6 7 8 9 10 11@app.route("/") def home(): return "Strona główna" @app.route("/about") def about(): return "O nas" @app.route("/contact") def contact(): return "Kontakt"
Dynamiczne parametry w URL
Python1 2 3 4 5 6 7 8 9 10 11@app.route("/user/<name>") def user(name): return f"Witaj, {name}!" @app.route("/post/<int:id>") def show_post(id): return f"Post ID: {id}" @app.route("/path/<path:subpath>") def show_subpath(subpath): return f"Subpath: {subpath}"
Możesz też określać typy parametrów:
<string:name>— domyślny typ (można pominąć)<int:id>— liczba całkowita<float:value>— liczba zmiennoprzecinkowa<path:subpath>— ścieżka (zezwala na slashe)
Obsługa różnych metod HTTP
Python1 2 3 4 5 6 7 8 9 10 11 12@app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": # Logika weryfikacji użytkownika i tworzenia sesji return "Zalogowano" # Wyświetl formularz logowania return ''' <form method="POST"> <input type="text" name="username"> <input type="submit" value="Login"> </form> '''
Generowanie URL
Python1 2 3 4 5 6 7 8 9from flask import url_for @app.route("/") def index(): return f"<a href='{url_for('user', name='Kacper')}'>Profil</a>" @app.route("/user/<name>") def user(name): return f"Witaj, {name}!"
Szablony HTML z Jinja2
Flask korzysta z silnika szablonów Jinja2, który pozwala tworzyć dynamiczne strony HTML.
Struktura projektu
Bash1 2 3 4 5 6 7 8app.py templates/ ├── index.html ├── about.html └── base.html static/ ├── style.css └── script.js
Podstawowe użycie szablonów
app.py:
Python1 2 3 4 5 6 7 8 9 10 11from flask import Flask, render_template app = Flask(__name__) @app.route("/") def home(): return render_template("index.html", title="Strona główna", users=["Kacper", "Anna"]) @app.route("/about") def about(): return render_template("about.html", title="O nas")
templates/index.html:
HTML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16<!DOCTYPE html> <html lang="pl"> <head> <title>{{ title }}</title> </head> <body> <h1>Witaj w Flask!</h1> <p>To dynamiczna treść wygenerowana przez Jinja2.</p> <ul> {% for user in users %} <li>{{ user }}</li> {% endfor %} </ul> </body> </html>
Dziedziczenie szablonów
templates/base.html:
HTML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21<!DOCTYPE html> <html lang="pl"> <head> <title>{% block title %}My App{% endblock %}</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> </head> <body> <nav> <a href="{{ url_for('home') }}">Strona główna</a> <a href="{{ url_for('about') }}">O nas</a> </nav> <main> {% block content %}{% endblock %} </main> <footer> <p>© 2025 My App</p> </footer> </body> </html>
templates/index.html:
HTML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18{% extends "base.html" %} {% block title %}{{ title }} - My App{% endblock %} {% block content %} <h1>Witaj w Flask!</h1> <p>To dynamiczna treść.</p> {% if users %} <ul> {% for user in users %} <li>{{ user }}</li> {% endfor %} </ul> {% else %} <p>Brak użytkowników.</p> {% endif %} {% endblock %}
Filtry Jinja2
Jinja2 oferuje wiele przydatnych filtrów:
HTML1 2 3 4 5{{ name|upper }} <!-- WIELKIE LITERY --> {{ description|truncate(50) }}<!-- Skróć do 50 znaków --> {{ date|date_format }} <!-- Formatowanie daty --> {{ value|default("Brak") }}<!-- Wartość domyślna --> {{ html|safe }}<!-- Wyłącz escaping HTML -->
Makra i includy
templates/macros.html:
HTML1 2 3 4 5 6{% macro render_user(user) %} <div class="user"> <h3>{{ user.name }}</h3> <p>{{ user.email }}</p> </div> {% endmacro %}
templates/index.html:
HTML1 2 3 4 5 6 7 8{% extends "base.html" %} {% from "macros.html" import render_user %} {% block content %} {% for user in users %} {{ render_user(user) }} {% endfor %} {% endblock %}
Formularze i dane POST
Obsługa formularzy w Flasku jest bardzo prosta. Możesz pobierać dane z request.form lub request.args.
Podstawowa obsługa formularza
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23from flask import Flask, request, render_template app = Flask(__name__) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form.get("username") password = request.form.get("password") if username and password: # Weryfikacja danych return f"Witaj, {username}!" else: return "Proszę wypełnić wszystkie pola", 400 return ''' <form method="POST"> <input type="text" name="username" placeholder="Login" required> <input type="password" name="password" placeholder="Hasło" required> <input type="submit" value="Zaloguj"> </form> '''
Obsługa plików
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33from flask import request from werkzeug.utils import secure_filename import os UPLOAD_FOLDER = 'uploads' ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route("/upload", methods=["GET", "POST"]) def upload_file(): if request.method == "POST": if 'file' not in request.files: return "Brak pliku", 400 file = request.files['file'] if file.filename == '': return "Nie wybrano pliku", 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return f"Plik {filename} został przesłany!" return ''' <form method="POST" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="Prześlij"> </form> '''
Flask-WTF dla zaawansowanych formularzy
Dla bardziej złożonych formularzy możesz skorzystać z biblioteki Flask-WTF, która zapewnia walidację danych i integrację z CSRF.
Bash1pip install Flask-WTF
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21from flask import Flask, render_template from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, Email, Length app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(min=3, max=20)]) password = PasswordField('Password', validators=[DataRequired()]) submit = SubmitField('Login') @app.route("/login", methods=["GET", "POST"]) def login(): form = LoginForm() if form.validate_on_submit(): username = form.username.data # Weryfikacja danych użytkownika i utworzenie sesji return f"Witaj, {username}!" return render_template("login.html", form=form)
templates/login.html:
HTML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19{% extends "base.html" %} {% block content %} <form method="POST"> {{ form.hidden_tag() }} <p> {{ form.username.label }}<br> {{ form.username(size=32) }} {% if form.username.errors %} <span style="color: red;">{{ form.username.errors[0] }}</span> {% endif %} </p> <p> {{ form.password.label }}<br> {{ form.password(size=32) }} </p> <p>{{ form.submit() }}</p> </form> {% endblock %}
Sesje i cookies
Flask ma wbudowany system sesji oparty o ciasteczka podpisane kluczem aplikacji.
Podstawowe użycie sesji
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22from flask import Flask, session, request app = Flask(__name__) app.secret_key = "sekretny-klucz-zmien-w-produkcji" @app.route("/set_user") def set_user(): session["username"] = "Kacper" session["user_id"] = 123 return "Zapisano użytkownika w sesji!" @app.route("/get_user") def get_user(): username = session.get("username", "Anonim") user_id = session.get("user_id") return f"Zalogowany: {username} (ID: {user_id})" @app.route("/logout") def logout(): session.pop("username", None) session.pop("user_id", None) return "Wylogowano"
Sesje są szyfrowane — dane użytkownika nie są przechowywane po stronie serwera, ale w bezpieczny sposób w ciasteczkach. Wymagają SECRET_KEY.
Konfiguracja sesji
Python1 2 3 4 5 6 7 8 9 10from datetime import timedelta app.config['SECRET_KEY'] = 'your-secret-key' app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=24) @app.route("/login", methods=["POST"]) def login(): session.permanent = True# Sesja wygasa po PERMANENT_SESSION_LIFETIME session["username"] = request.form["username"] return "Zalogowano"
Bezpośrednia praca z cookies
Python1 2 3 4 5 6 7 8 9 10 11 12from flask import make_response @app.route("/set_cookie") def set_cookie(): resp = make_response("Cookie ustawione") resp.set_cookie("preference", "dark_mode", max_age=60*60*24*30)# 30 dni return resp @app.route("/get_cookie") def get_cookie(): preference = request.cookies.get("preference", "light_mode") return f"Preferencja: {preference}"
Integracja z bazą danych
Flask nie ma własnego ORM (jak Django), ale doskonale współpracuje z SQLAlchemy lub Peewee.
Przykład z SQLAlchemy
Instalacja:
Bash1pip install flask-sqlalchemy
app.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39from flask import Flask, render_template from flask_sqlalchemy import SQLAlchemy from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) def __repr__(self): return f"<User {self.name}>" # Inicjalizacja bazy danych with app.app_context(): db.create_all() @app.route("/add/<name>/<email>") def add_user(name, email): user = User(name=name, email=email) db.session.add(user) db.session.commit() return f"Dodano użytkownika {name}" @app.route("/users") def list_users(): users = User.query.all() return render_template("users.html", users=users) @app.route("/user/<int:user_id>") def get_user(user_id): user = User.query.get_or_404(user_id) return render_template("user.html", user=user)
Ten przykład tworzy prostą bazę danych SQLite i dodaje użytkowników poprzez endpoint.
Migracje z Flask-Migrate
Bash1pip install Flask-Migrate
Python1 2 3from flask_migrate import Migrate migrate = Migrate(app, db)
Bash1 2 3flask db init flask db migrate -m "Initial migration" flask db upgrade
Refaktoryzacja – Blueprints
Kiedy aplikacja rośnie, warto ją modularnie rozdzielić. W Flasku służą do tego Blueprints — odpowiednik routerów w FastAPI lub aplikacji w Django.
Struktura projektu
Bash1 2 3 4 5 6 7 8app/ ├── __init__.py ├── main/ │ ├── __init__.py │ └── routes.py └── users/ ├── __init__.py └── routes.py
app/main/routes.py:
Python1 2 3 4 5 6 7 8 9 10 11from flask import Blueprint, render_template main = Blueprint("main", __name__) @main.route("/") def home(): return render_template("index.html", title="Strona główna") @main.route("/about") def about(): return render_template("about.html", title="O nas")
app/users/routes.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14from flask import Blueprint, render_template from app.models import User users = Blueprint("users", __name__, url_prefix="/users") @users.route("/") def list_users(): users_list = User.query.all() return render_template("users/list.html", users=users_list) @users.route("/<int:user_id>") def show_user(user_id): user = User.query.get_or_404(user_id) return render_template("users/detail.html", user=user)
app/init.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16from flask import Flask from app.main.routes import main from app.users.routes import users def create_app(): app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' from app.models import db db.init_app(app) app.register_blueprint(main) app.register_blueprint(users) return app
run.py:
Python1 2 3 4 5 6from app import create_app app = create_app() if __name__ == "__main__": app.run(debug=True)
To podejście pozwala skalować aplikację bez utraty przejrzystości. Każdy blueprint może mieć własne szablony, statyczne pliki i konfigurację.
Konfiguracja i środowiska
Flask wspiera różne środowiska (dev, test, production). Wystarczy przygotować plik config.py:
config.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18import os class Config: SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key' SQLALCHEMY_TRACK_MODIFICATIONS = False class DevelopmentConfig(Config): DEBUG = True SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or 'sqlite:///dev.db' class TestingConfig(Config): TESTING = True SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' WTF_CSRF_ENABLED = False class ProductionConfig(Config): DEBUG = False SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'postgresql://user:pass@localhost/prod'
app/init.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24from flask import Flask from config import Config, DevelopmentConfig, TestingConfig, ProductionConfig config = { 'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig } def create_app(config_name='development'): app = Flask(__name__) app.config.from_object(config[config_name]) # Inicjalizacja rozszerzeń from app.models import db db.init_app(app) # Rejestracja blueprintów from app.main.routes import main from app.users.routes import users app.register_blueprint(main) app.register_blueprint(users) return app
run.py:
Python1 2 3 4 5 6 7 8import os from app import create_app config_name = os.getenv('FLASK_ENV', 'development') app = create_app(config_name) if __name__ == "__main__": app.run()
Zmienne środowiskowe
Używaj .env do przechowywania konfiguracji:
Bash1pip install python-decouple
.env:
INI1 2 3SECRET_KEY=super-secret-key DATABASE_URL=postgresql://user:pass@localhost/db FLASK_ENV=development
config.py:
Python1 2 3 4 5from decouple import config class Config: SECRET_KEY = config('SECRET_KEY') SQLALCHEMY_DATABASE_URI = config('DATABASE_URL')
Testowanie aplikacji Flask
Testy w Flasku są proste i szybkie dzięki pytest lub unittest.
Podstawowe testy z unittest
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24import unittest from app import create_app class FlaskTestCase(unittest.TestCase): def setUp(self): self.app = create_app('testing') self.client = self.app.test_client() self.app_context = self.app.app_context() self.app_context.push() def tearDown(self): self.app_context.pop() def test_home(self): response = self.client.get("/") self.assertEqual(response.status_code, 200) self.assertIn(b"Witaj", response.data) def test_login(self): response = self.client.post("/login", data={ "username": "test", "password": "test123" }) self.assertEqual(response.status_code, 200)
Testy z pytest
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21import pytest from app import create_app @pytest.fixture def client(): app = create_app('testing') with app.test_client() as client: with app.app_context(): yield client def test_home(client): response = client.get("/") assert response.status_code == 200 assert b"Witaj" in response.data def test_login(client): response = client.post("/login", data={ "username": "test", "password": "test123" }) assert response.status_code == 200
Uruchom testy:
Bash1pytest -v
Wdrożenie aplikacji Flask
Gotową aplikację Flask możesz wdrożyć na różne sposoby:
Render, Railway, Fly.io
Szybkie platformy cloudowe, które automatycznie wykrywają Flask:
requirements.txt:
Bash1 2Flask==3.0.0 gunicorn==21.2.0
Procfile (dla Railway/Render):
Bash1web: gunicorn app:app
Docker
Przykład Dockerfile:
Dockerfile1 2 3 4 5 6 7 8 9 10FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["gunicorn", "-b", "0.0.0.0:8000", "--workers", "4", "app:app"]
Gunicorn + Nginx
Uruchomienie z Gunicorn:
Bash1 2pip install gunicorn gunicorn -w 4 -b 0.0.0.0:8000 app:app
Konfiguracja Nginx:
NGINX1 2 3 4 5 6 7 8 9 10server { listen 80; server_name example.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
Najlepsze praktyki
Poniżej znajdziesz zestaw najlepszych praktyk do tworzenia aplikacji Flask:
1. Oddziel logikę biznesową od widoków
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18# services/user_service.py class UserService: @staticmethod def create_user(name, email): # Logika biznesowa user = User(name=name, email=email) db.session.add(user) db.session.commit() return user # routes/users.py from app.services.user_service import UserService @users.route("/", methods=["POST"]) def create_user(): data = request.json user = UserService.create_user(data['name'], data['email']) return jsonify(user.to_dict()), 201
2. Używaj Blueprints
Modularna architektura to klucz do skalowalności. Każdy blueprint powinien odpowiadać za jedną funkcjonalność.
3. Zabezpieczaj formularze (CSRF, XSS)
Używaj Flask-WTF do ochrony przed CSRF:
Python1 2 3from flask_wtf.csrf import CSRFProtect csrf = CSRFProtect(app)
W szablonach:
HTML1 2 3 4<form method="POST"> {{ csrf_token() }} <!-- pola formularza --> </form>
4. Korzystaj z .env do przechowywania sekretów
Nigdy nie commituj kluczy i haseł do repozytorium. Używaj zmiennych środowiskowych.
5. Stosuj testy jednostkowe i integracyjne
Pisz testy dla każdego endpointu i logiki biznesowej.
6. Zadbaj o logowanie i obsługę błędów
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20import logging from logging.handlers import RotatingFileHandler if not app.debug: file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240, backupCount=10) file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) file_handler.setLevel(logging.INFO) app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) @app.errorhandler(404) def not_found_error(error): return render_template('errors/404.html'), 404 @app.errorhandler(500) def internal_error(error): db.session.rollback() return render_template('errors/500.html'), 500
7. Używaj migracji bazy danych
Flask-Migrate pozwala zarządzać zmianami w schemacie bazy danych.
8. Cache'uj odpowiedzi dla lepszej wydajności
Bash1pip install Flask-Caching
Python1 2 3 4 5 6 7 8 9from flask_caching import Cache cache = Cache(app, config={'CACHE_TYPE': 'simple'}) @app.route("/expensive") @cache.cached(timeout=300) def expensive_operation(): # Kosztowna operacja return expensive_result
Podsumowanie
Flask to framework, który daje Ci pełną kontrolę i prostotę. Jest lekki, intuicyjny i pozwala na szybkie prototypowanie.
Świetnie sprawdza się w:
- małych i średnich aplikacjach,
- API backendowych,
- serwisach do automatyzacji lub dashboardach,
- prototypach i MVP (Minimum Viable Product).
Najważniejsze cechy Flask:
- Minimalizm — tylko to, czego potrzebujesz
- Elastyczność — pełna kontrola nad strukturą
- Rozszerzalność — bogaty ekosystem rozszerzeń
- Prostota — łatwa nauka i czytelny kod
- Wsparcie społeczności — duża społeczność i dokumentacja
Jeśli szukasz frameworka, który "nie przeszkadza" i pozwala budować aplikacje po swojemu, Flask to idealny wybór. Zacznij od prostych endpointów, stopniowo dodawaj funkcjonalności i rozbudowuj aplikację zgodnie z potrzebami projektu.



