Na rozmowach z Flaskiem najczęściej pada kilkanaście pytań: czym Flask różni się od Django i FastAPI, jak działa routing i obiektrequest, czym jestjsonify, jak działają szablony Jinja2, czym są blueprinty oraz kontekst aplikacji i żądania. Rekruter nie sprawdza, czy znasz wszystkie rozszerzenia, tylko czy rozumiesz, co dzieje się z żądaniem od przyjścia do odpowiedzi. Dobra odpowiedź to jedno zdanie definicji i krótki przykład kodu.
Flask to jeden z najstarszych i najpopularniejszych mikroframeworków Pythona, więc nadal trafia się w ofertach, zwłaszcza w istniejących projektach i mniejszych usługach. Rozmowa o nim rzadko jest egzaminem z dokumentacji. Najczęściej to te same kilkanaście tematów, przy których rekruter sprawdza, czy rozumiesz, jak framework obsługuje żądanie, a nie czy pamiętasz nazwy dekoratorów. Poniżej znajdziesz pytania, które wracają na rozmowach, razem z tym, jak na nie odpowiadać. Jeśli dopiero zaczynasz, zacznij od artykułu o tym, czym jest Flask i jak stworzyć pierwszą aplikację, a ten tekst potraktuj jako listę kontrolną. Sam język sprawdzimy w pytaniach rekrutacyjnych z Pythona. Framework o innej filozofii opisujemy w artykule o pytaniach z Django, a nowocześniejsze, asynchroniczne podejście w tekście o pytaniach z FastAPI.
Przykłady z tego artykułu sprawdziliśmy w działającej aplikacji na Flasku 3.1 z Pythonem 3.13: testowaliśmy żądania, JSON, routing, szablony, sesje i konteksty. Wyniki w tekście, np. kody statusu i komunikaty błędów, pochodzą z tych uruchomień.
Jak odpowiadać na pytania rekrutacyjne z Flaska?
Najlepiej w trzech krokach: jedno zdanie definicji, krótki przykład (kilka linijek kodu albo sytuacja z projektu) i informacja, kiedy to ma znaczenie w praktyce. Taka odpowiedź zajmuje 30–60 sekund i pokazuje, że rozumiesz mechanizm, a nie wkułeś formułkę. Jeśli czegoś nie wiesz, powiedz, co wiesz, i rozumuj na głos. Rekruterzy oceniają sposób myślenia, a nie tylko wynik.
Pytania są ułożone od podstaw do tematów, które częściej pojawiają się przy ofertach na mid-poziom.
Podstawy Flaska
Czym jest Flask i czym różni się od Django i FastAPI?
Flask to mikroframework do tworzenia aplikacji webowych i API w Pythonie. „Mikro” nie znaczy „mały”, tylko że rdzeń zawiera routing, obsługę żądań, szablony Jinja2 i serwer deweloperski, a resztę (baza danych, formularze, uwierzytelnianie) dobierasz z rozszerzeń. Django jest dużym frameworkiem „z bateriami” z ORM i panelem administracyjnym, a FastAPI skupia się na API, walidacji przez Pydantic i asynchroniczności. Szersze porównanie znajdziesz w artykule Django, Flask czy FastAPI.
Co to jest WSGI i jak Flask z nim współpracuje?
WSGI (Web Server Gateway Interface) to standard, który opisuje, jak serwer HTTP przekazuje żądanie do aplikacji w Pythonie i odbiera odpowiedź. Aplikacja Flaska jest właśnie taką aplikacją WSGI. W pracy lokalnej uruchamiasz ją wbudowanym serwerem deweloperskim, a na produkcji serwerem WSGI, takim jak Gunicorn, często za serwerem nginx. Takie zestawienie opisujemy z dokumentacji, nie testowaliśmy go w tym artykule.
Jak wygląda minimalna aplikacja we Flasku?
from flask import Flask
app = Flask(__name__)
@app.get("/health")
def health():
return {"status": "ok"}
Obiekt Flask(__name__) to aplikacja, a dekorator @app.get rejestruje funkcję widoku (view) pod danym adresem. Widok, który zwraca słownik, automatycznie zamienia się na odpowiedź JSON. W teście taki widok zwrócił kod 200 i Content-Type: application/json.
Routing i żądania
Jak pobrać dane z żądania: query string, formularz i JSON?
| Skąd pochodzą dane | Jak je odczytać |
|---|---|
Query string (?q=flask) | request.args.get("q") |
| Formularz HTML | request.form |
| Ciało JSON | request.get_json() |
| Nagłówki | request.headers |
W teście GET /echo?q=flask zwróciło {"q": "flask"}, POST z formularzem dał {"a": "b"} w request.form, a POST z json={"k": 1} dał słownik w get_json(). Ważny szczegół: gdy klient wyśle JSON z nagłówkiem text/plain, get_json(silent=True) zwraca None, a samo get_json() kończy się kodem 415, natomiast niepoprawny JSON z poprawnym nagłówkiem daje 400.
from flask import request, jsonify
@app.post("/echo")
def echo():
data = request.get_json(silent=True) or {}
return jsonify(received=data), 201
Czym różni się jsonify od json.dumps?
jsonify buduje pełną odpowiedź HTTP z nagłówkiem Content-Type: application/json, a json.dumps zwraca tylko tekst, który Flask potraktuje jak zwykły HTML. Dlatego do zwracania JSON używasz jsonify albo, w nowszych wersjach Flaska (testowaliśmy 3.1), po prostu zwracasz słownik lub listę. Kod statusu dodajesz krotką: return jsonify(...), 201.
Jak działają parametry w adresie i konwertery?
W adresie umieszczasz zmienną w nawiasach: /users/<int:user_id>. Konwerter (int, float, uuid, path) zamienia typ i odrzuca niepasujące wartości. W teście /users/7 zwróciło {"id": 7}, a /users/abc skończyło się kodem 404, bo adres nie pasował do konwertera. Opcjonalny parametr uzyskujesz przez dwie trasy do tej samej funkcji albo wartość domyślną.
Skąd bierze się 405 Method Not Allowed?
Trasa domyślnie obsługuje tylko GET (oraz HEAD i OPTIONS). Jeśli wysyłasz inną metodę, np. POST, Flask zwraca 405. W teście POST na trasę zdefiniowaną tylko dla GET dało właśnie 405. Dozwolone metody ustawiasz w methods=[...] lub dekoratorami @app.post, @app.get.
Jak działa ukośnik na końcu adresu?
Zachowanie zależy od definicji trasy. Gdy trasa jest zapisana z ukośnikiem (/page/), a klient wywoła /page, Flask przekierowuje go na wersję z ukośnikiem (w teście kod 308), ale gdy trasa jest zapisana bez ukośnika (/plain), a klient wywoła /plain/, odpowiedzią jest 404. Warto to odróżnić od FastAPI, który przy trasie z ukośnikiem wywołanej bez niego odpowiada kodem 307, a nie 308.
Do czego służy url_for i redirect?
url_for("nazwa_widoku", argument=...) buduje adres na podstawie nazwy funkcji widoku, więc zmiana ścieżki nie psuje linków w szablonach i kodzie. redirect zwraca odpowiedź z przekierowaniem. W teście redirect(url_for("user", user_id=5)) zwróciło kod 302 i adres /users/5.
from flask import redirect, url_for
@app.get("/go")
def go():
return redirect(url_for("user", user_id=5))
Szablony Jinja2
Jak działają szablony w Flasku i skąd błąd TemplateNotFound?
render_template("plik.html", zmienna=wartość) renderuje szablon Jinja2 z katalogu templates obok pliku z aplikacją. Błąd TemplateNotFound oznacza zwykle, że katalog nazywa się inaczej, leży w złym miejscu albo nazwa pliku jest błędna. W teście brak szablonu zakończył się kodem 500 (w trybie bez propagowania wyjątków) i wyjątkiem TemplateNotFound.
Co robi autoescape w Jinja2 i kiedy użyć |safe?
Jinja2 domyślnie zamienia znaki specjalne HTML w zmiennych na encje, co chroni przed atakiem XSS. W teście dla wartości <b>x</b> szablon {{ name }} wyświetlił <b>x<b>, a {{ name|safe }} pozostawił znaczniki jako HTML. Filtr |safe wolno stosować tylko wtedy, gdy masz pewność, że treść jest zaufana, np. wygenerowana przez Ciebie. Dla danych od użytkownika zostawiasz domyślne zachowanie.
<p>{{ name }}</p> <!-- bezpieczne: znaki są zamieniane na encje -->
<p>{{ name|safe }}</p> <!-- niebezpieczne dla danych od użytkownika -->
Struktura aplikacji i konteksty
Czym są blueprinty i po co się je stosuje?
Blueprint (blueprint) to sposób na podzielenie aplikacji na moduły: każdy ma własne trasy, szablony i prefiks adresu, a główny plik tylko je rejestruje. Dzięki temu aplikacja nie rośnie w jednym pliku. W teście blueprint z prefiksem /api odpowiedział na /api/ping.
from flask import Blueprint
api = Blueprint("api", __name__, url_prefix="/api")
@api.get("/ping")
def ping():
return {"pong": True}
app.register_blueprint(api)
Co to jest wzorzec application factory?
To funkcja create_app(), która tworzy i konfiguruje aplikację, zamiast robić to na poziomie modułu. Pozwala utworzyć kilka instancji z różną konfiguracją, np. osobną dla testów, a rejestrację blueprintów i rozszerzeń robisz w jednym miejscu. W teście właśnie taka fabryka dawała aplikację z konfiguracją TESTING=True.
Czym są kontekst aplikacji i kontekst żądania oraz g i current_app?
Flask udostępnia obiekty globalne (request, session, g, current_app), które wskazują na aktualne żądanie i aplikację. Działają tylko wtedy, gdy trwa odpowiedni kontekst. g to miejsce na dane ważne w obrębie jednego żądania (np. aktualny użytkownik), a current_app to aktualna aplikacja. W teście wartość ustawiona w g w funkcji before_request była widoczna w widoku. Poza kontekstem użycie current_app kończyło się błędem RuntimeError: Working outside of application context., a w bloku with app.app_context(): działało.
from flask import g, current_app
@app.before_request
def set_user():
g.user = "anna"
@app.get("/me")
def me():
return {"user": g.user, "app": current_app.name}
Jak działają sesje i po co SECRET_KEY?
Sesja przechowuje dane użytkownika między żądaniami w podpisanym ciasteczku. Podpis wymaga SECRET_KEY, bo to on chroni ciasteczko przed podrobieniem. W teście próba zapisu do session w aplikacji bez klucza zakończyła się błędem RuntimeError: The session is unavailable because no secret key was set. Klucz powinien być długi i losowy, nie wpisany na stałe w repozytorium.
Jak obsłużyć błędy, np. 404, i własne wyjątki?
Przez @app.errorhandler(kod_lub_wyjątek): rejestrujesz funkcję, która zwróci odpowiedź w miejscu domyślnej strony błędu. Błąd z poziomu widoku zgłaszasz przez abort(kod). W teście własny handler dla 404 zwrócił {"error": "nie ma"}, a abort(403) dał kod 403.
from flask import abort
@app.errorhandler(404)
def not_found(error):
return {"error": "nie ma"}, 404
@app.get("/boom")
def boom():
abort(403)
Praktyka i produkcja
Jak testuje się aplikację Flask?
Przez app.test_client(), który wywołuje aplikację bez uruchamiania serwera, zwykle razem z pytest. Wszystkie przykłady w tym artykule były sprawdzone właśnie w ten sposób. W testach warto użyć fabryki aplikacji z konfiguracją TESTING=True.
def test_health():
client = create_app().test_client()
response = client.get("/health")
assert response.status_code == 200
Dlaczego Flask ostrzega „Do not use the development server in a production environment”?
Serwer wbudowany we Flaska służy do pracy lokalnej: nie jest zaprojektowany na duży ruch, bezpieczeństwo ani stabilność. Na produkcji aplikację uruchamia się przez serwer WSGI, np. Gunicorn, zwykle za serwerem nginx. Jest to jedno z częstszych pytań początkujących na Stack Overflow. Różnice konfiguracji opisujemy z dokumentacji, nie testowaliśmy ich.
Jak włączyć CORS we Flasku?
Najczęściej przez rozszerzenie Flask-CORS, które dodaje odpowiednie nagłówki do odpowiedzi. Zasada działania jest taka sama jak w innych frameworkach: przeglądarka blokuje wywołania z innej domeny, jeśli serwer nie wyśle Access-Control-Allow-Origin. Samego rozszerzenia nie testowaliśmy.
Jak połączyć Flask z bazą danych?
Najczęściej przez rozszerzenie Flask-SQLAlchemy, które integruje SQLAlchemy z konfiguracją aplikacji, albo przez bezpośrednie użycie SQLAlchemy. Migracje schematu obsługuje zwykle Alembic (przez Flask-Migrate). W tym artykule nie testowaliśmy tej części. Przy pytaniach o bazę liczy się to, że rozumiesz sesję, transakcje i zapytania, a nie nazwę rozszerzenia.
Zadania z kodem i mini-test
Jakie zadania z Flaska pojawiają się na rozmowach?
Najczęściej to krótkie zadania typu: „napisz endpoint, który przyjmuje JSON i zwraca listę”, „zrób prosty formularz z szablonem”, „podziel aplikację na blueprinty” albo „znajdź błąd w tym kodzie”. Przy takich zadaniach liczy się nie szybkość pisania, tylko to, czy rozumiesz, dlaczego wybierasz dane rozwiązanie. Mów na głos, jak Flask potraktuje Twój kod.
Co jest nie tak w tym kodzie? Mini-test na rozgrzewkę
from flask import Flask, request, session
app = Flask(__name__)
@app.route("/login", methods=["POST"])
def login():
data = request.get_json()
session["user"] = data["name"]
return "<p>Witaj, " + data["name"] + "</p>"
Są tu co najmniej trzy problemy:
- Brak
SECRET_KEY. Zapis dosessionzakończy się błędemRuntimeError: The session is unavailable because no secret key was set.Trzeba ustawićapp.config["SECRET_KEY"](nie w kodzie, tylko ze zmiennej środowiskowej). - Niebezpieczne sklejanie HTML. Wartość od użytkownika trafia do odpowiedzi bez zamiany znaków specjalnych, co otwiera drogę do XSS. Lepiej użyć
render_template, który domyślnie escapuje zmienne, albo zwrócić JSON. - Brak walidacji wejścia.
request.get_json()bezsilent=Truezwróci 415 przy złym nagłówku i 400 przy złym JSON, adata["name"]rzuciKeyError(kod 500), jeśli klucza brakuje. Trzeba sprawdzić, czy dane są kompletne, i zwrócić kod 400 z czytelnym komunikatem.
Jak przygotować się do rozmowy z Flaskiem?
- Zbuduj małe API od zera. Kilka tras, JSON, szablon Jinja2, blueprint, sesja i testy z
test_client. Zacznij od pierwszej aplikacji we Flasku. - Zobacz, co się psuje. Wywołaj
current_apppoza kontekstem, użyj sesji bez klucza, wyślij JSON z błędnym nagłówkiem. Zobaczysz komunikaty, o które pytają rekruterzy. - Przećwicz różnice między frameworkami. Zastanów się, co z tego samego endpointu zrobiłby Django i FastAPI (np. walidację, dokumentację, async). Takie porównanie często pada na rozmowie.
- Przećwicz odpowiedzi na głos. Dla każdego pytania z tego artykułu sformułuj w 30 sekund definicję i przykład.
Na czym juniorzy najczęściej tracą punkty?
Nie na trudnych tematach, tylko na trzech drobiazgach, które wychodzą dopiero przy pytaniach uzupełniających:
- Mylenie kontekstów. Odpowiedź „g to zmienna globalna” kończy temat. Wyjaśnij, że
gżyje w obrębie jednego żądania, acurrent_appdziała tylko w kontekście aplikacji. - Sesje bez wiedzy, jak działają. Powiedz, że dane są w podpisanym ciasteczku, a
SECRET_KEYchroni je przed podrobieniem. Nie myl sesji z bazą danych. - Serwer deweloperski na produkcji. Pokaż, że wiesz, po co jest serwer WSGI i czemu
app.run()nie wystarcza.
Jak brzmi dobra odpowiedź, a jak słaba?
Weźmy pytanie o jsonify:
| Odpowiedź | Dlaczego | |
|---|---|---|
| Słaba | „jsonify zamienia słownik na JSON.” | To samo robi json.dumps. Pytanie uzupełniające („to po co jsonify?”) od razu to pokaże. |
| Dobra | „jsonify buduje odpowiedź z nagłówkiem Content-Type: application/json, a json.dumps zwraca sam tekst, który Flask potraktuje jak HTML. W nowszych wersjach mogę też po prostu zwrócić słownik.” | Jest różnica w zachowaniu, konsekwencja i aktualna wersja. |
Ten sam schemat działa przy każdym pytaniu z tego artykułu: definicja, mechanizm, przykład użycia.
Co zrobić, gdy nie znasz odpowiedzi?
Nie zgaduj i nie milcz. Powiedz, co wiesz na pewno, a potem rozumuj na głos, np.: „Nie pamiętam dokładnej nazwy rozszerzenia do CORS, ale wiem, że szukam sposobu na dodanie nagłówka Access-Control-Allow-Origin, więc sprawdziłbym to w dokumentacji.” Taka odpowiedź pokazuje, że potrafisz sobie poradzić w pracy, a umiejętność znalezienia odpowiedzi jest w codziennej pracy ważniejsza niż pamięć do szczegółów.
Jeśli dopiero planujesz ścieżkę nauki, pamiętaj, że w naszym programie Pythona uczymy Django, a nie Flaska. Zasady, które poznajesz we Flasku (żądanie i odpowiedź, routing, szablony, sesje), przenoszą się jednak między frameworkami. Plan nauki opisujemy na ścieżce Pythona z mentorem. Ogólny przebieg całego procesu rekrutacji opisujemy w poradniku jak przygotować się do rekrutacji na juniora.


