Using a database with Flask-SQLAlchemy

Flask · Practical · 15 min

Most Flask apps store data in SQL, and Flask-SQLAlchemy wires sessions to the request lifecycle for you.

Flask has no database layer of its own. The most common choice is Flask-SQLAlchemy (pip install flask-sqlalchemy), which integrates SQLAlchemy 2.x:

  • db = SQLAlchemy(model_class=Base) where Base subclasses DeclarativeBase
  • configure SQLALCHEMY_DATABASE_URI, such as sqlite:///app.db or postgresql+psycopg://...
  • models subclass db.Model with typed columns: id: Mapped[int] = mapped_column(primary_key=True)
  • queries use SQLAlchemy 2.0 style: db.session.execute(db.select(User).where(User.name == "ann")).scalars().all(), or db.session.scalars(...)
  • shortcuts for views: db.get_or_404(User, id), db.first_or_404(stmt), db.paginate(stmt, per_page=20)

db.session is scoped to the app context, so each request gets its own session, and Flask-SQLAlchemy removes it when the request ends. You still decide when to write: db.session.add(obj) then db.session.commit(). If something fails mid-way, db.session.rollback().

db.create_all() creates missing tables, which is fine for prototypes and tests but never alters existing ones. Real projects use Flask-Migrate (Alembic) to generate and apply schema migrations.

Avoid the legacy User.query.filter_by(...) API in new code; it still works, but SQLAlchemy 2.0 style db.select is the documented approach.

If you prefer no ORM, Flask's tutorial shows plain sqlite3 with a connection stored in g and closed in teardown_appcontext.

Example

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase): pass
db = SQLAlchemy(model_class=Base)

class Note(db.Model):
    id: Mapped[int] = mapped_column(primary_key=True)
    text: Mapped[str]
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
db.init_app(app)
@app.post("/notes/<text>")
def add(text):
    note = Note(text=text)
    db.session.add(note); db.session.commit()
    return {"id": note.id}, 201
@app.get("/notes/<int:nid>")
def show(nid):
    return {"text": db.get_or_404(Note, nid).text}
with app.app_context(): db.create_all()
c = app.test_client()
print(c.post("/notes/hello").json, c.get("/notes/1").json, c.get("/notes/9").status_code)
# {'id': 1} {'text': 'hello'} 404

Remember

  • Flask-SQLAlchemy gives db.Model, db.session and Flask-aware helpers
  • Query with db.select(...) and db.session.execute/scalars (SQLAlchemy 2.0 style)
  • Commit explicitly; use Flask-Migrate for schema changes

Watch out for

  • Relying on db.create_all() to apply schema changes to an existing database; it only creates missing tables.

Check yourself

  1. You add a column to a model in an app with existing data. What updates the database schema?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/patterns/sqlalchemy/