Flask contexts, factories & patterns
Questions in this set 10
- 01Explain the application context and the request context.
- 02What is the application factory pattern and why does it matter?
- 03Blueprints — what do they give you, and what are their limits?
- 04How does Flask handle sessions, and is the default safe?
- 05Flask vs Django vs FastAPI — when do you pick Flask?
- 06How do you connect to a database and manage sessions in Flask?
- 07What are the common security mistakes in a Flask app?
- 08How do you handle configuration and secrets?
- 09How do you deploy a Flask app?
- 10How do you test a Flask app?
Explain the application context and the request context.
Flask uses context-locals — objects that look global but resolve to the current request's data. There are two stacks:
- App context —
current_appandg. Pushed automatically during a request, and manually (with app.app_context():) in CLI commands, tests and background threads. - Request context —
requestandsession. Pushed per request; contains everything about the incoming HTTP call.
with app.app_context():
db.create_all() # needs current_app, but there is no request
with app.test_request_context("/users?page=2"):
assert request.args["page"] == "2"g is per-request, not global across requests — a persistently misunderstood name. Since Flask 2.2 g is tied to the app context, so a fresh one exists for every request and every CLI invocation.
"Working outside of application context" means you touched current_app/g where no context is pushed: a background thread, a module-level statement, or a Celery task. Push one explicitly, or pass what you need as an argument.
What is the application factory pattern and why does it matter?
def create_app(config_object="config.Production") -> Flask:
app = Flask(__name__)
app.config.from_object(config_object)
app.config.from_prefixed_env() # FLASK_* environment variables
db.init_app(app) # extensions created at module level,
migrate.init_app(app, db) # bound to the app here
login_manager.init_app(app)
from .users import bp as users_bp
app.register_blueprint(users_bp, url_prefix="/users")
@app.errorhandler(404)
def not_found(e): return jsonify(error="not_found"), 404
return appWhy it matters: you can create multiple app instances with different configs (a fresh app per test), you avoid import-time side effects, and you break circular imports (blueprints imported inside the factory). A module-level app = Flask(__name__) makes testing and configuration painful — this is the single most common Flask code-review comment.
Blueprints — what do they give you, and what are their limits?
A blueprint is a deferred collection of routes, error handlers, template folders and static files, registered onto an app (possibly multiple times, with different prefixes). They give you modularity and URL namespacing (url_for("users.detail", id=1)).
Limits worth knowing: a blueprint is not an application — it has no config of its own, before_request on a blueprint only fires for its own routes (use before_app_request for global), and blueprints cannot be unregistered. For genuinely independent apps, mount separate WSGI apps with DispatcherMiddleware instead.
How does Flask handle sessions, and is the default safe?
The default session is a client-side cookie, signed with SECRET_KEY using itsdangerous. Signed means tamper-proof, not encrypted — anyone can base64-decode and read it. So: never put anything secret in the session, keep it small (4 KB cookie limit), and note there is no server-side revocation — you cannot log someone out except by rotating the secret (which logs out everyone).
For anything real, use server-side sessions (Flask-Session with Redis) so you get revocation, size, and no data exposure. Set SESSION_COOKIE_SECURE, HTTPONLY and SAMESITE="Lax", and give the cookie a lifetime.
Flask vs Django vs FastAPI — when do you pick Flask?
Flask is a microframework: routing, templating and a WSGI core, with everything else (ORM, auth, admin, migrations, forms) chosen by you. Pick it when you want that control, when the service is small and focused, when you are wrapping an existing library as an HTTP service, or when the team already has a Flask ecosystem.
Do not pick it when you would end up rebuilding Django (choose Django) or when you need heavy async I/O and typed schemas (choose FastAPI). Flask has async view support since 2.0, but the extension ecosystem is largely synchronous and each async view still runs in an event loop per request under WSGI — it is not a real async stack.
How do you connect to a database and manage sessions in Flask?
With Flask-SQLAlchemy, the extension scopes a session to the app context and removes it at teardown. Without it, do the same manually:
engine = create_engine(URL, pool_size=5, max_overflow=10, pool_pre_ping=True)
SessionLocal = scoped_session(sessionmaker(bind=engine))
@app.teardown_appcontext
def remove_session(exc=None):
SessionLocal.remove() # essential: otherwise connections leak per requestpool_pre_ping=True avoids "MySQL server has gone away" after idle timeouts. Never create an engine per request. And commit or roll back explicitly — a session left dirty at teardown holds a transaction and its locks open.
What are the common security mistakes in a Flask app?
SECRET_KEYhardcoded or defaulted — signs sessions and CSRF tokens; leaking it means session forgery.debug=Truein production — the Werkzeug debugger allows arbitrary code execution through the browser console. This is a real, exploited vulnerability.- Jinja
|safeorMarkup()on user input — XSS. Autoescaping is on for.htmltemplates only, so a.txtor string template does not escape. send_file/send_from_directorywith an unsanitised path — directory traversal. Usesafe_join/send_from_directorycorrectly.- String-formatted SQL instead of bound parameters.
- No CSRF protection on form POSTs (Flask has none built in — use Flask-WTF).
How do you handle configuration and secrets?
app.config.from_object() for defaults per environment, from_prefixed_env() or from_envvar() to layer environment values on top, and never commit secrets. In production, secrets come from the environment or a secret manager and are read once at startup. Config classes (Base/Dev/Prod) keep it readable, and app.config["ENV_NAME"] lets code branch when it must — though branching on environment inside business logic is a smell.
How do you deploy a Flask app?
Never app.run() — that is the development server: single-threaded by default, no security hardening, no process management. Use Gunicorn (gunicorn -w 4 -k gthread --threads 4 "app:create_app()") or uWSGI behind nginx/a load balancer. Workers ≈ 2*cores+1; use threads or gevent workers if the workload is I/O-bound and the code is thread-safe. Add a /healthz, log to stdout, set PROXY_FIX (werkzeug.middleware.proxy_fix.ProxyFix) so request.remote_addr and the scheme are correct behind a proxy, and run migrations as a release step.
How do you test a Flask app?
@pytest.fixture
def app():
app = create_app("config.Testing")
with app.app_context():
db.create_all(); yield app; db.drop_all()
@pytest.fixture
def client(app): return app.test_client()
def test_login(client):
r = client.post("/login", data={"email": "a@b.com", "password": "x"},
follow_redirects=True)
assert r.status_code == 200
with client.session_transaction() as s: # inspect/modify the session in tests
assert s["user_id"] == 1The factory makes each test a fresh app; test_client gives you a request context without a server; session_transaction lets you set up an authenticated state without going through the login flow every time.