Declaring a Model Class
A Flask-SQLAlchemy model is a Python class that inherits from db.Model, where each class attribute assigned a db.Column(...) maps to a table column, and SQLAlchemy infers the table name automatically from the class name (converted to lowercase, e.g. User becomes user) unless you override it with __tablename__. Every model needs a primary key column, conventionally id = db.Column(db.Integer, primary_key=True), which SQLAlchemy uses to identify and track individual rows for updates, deletes, and relationship joins.
Cricket analogy: Declaring a model is like designing a scorecard template before the tour starts — 'Runs', 'Balls Faced', and 'Wickets' are columns, and the player's unique registration number acts as the primary key row identifier.
from extensions import db
from datetime import datetime
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f'<User {self.username}>'Column Types and Constraints
Common column types include db.Integer, db.String(length), db.Text for unbounded text, db.Boolean, db.DateTime, and db.Float, and each db.Column() accepts keyword arguments that become SQL constraints: nullable=False enforces NOT NULL, unique=True adds a unique index, and default= supplies a Python-side default value (or server_default= for a database-side default) applied when no value is explicitly set. Choosing String(80) versus Text matters — String maps to VARCHAR(n) with a length cap enforced at the database level, while Text is unbounded and better suited for long content like blog post bodies.
Cricket analogy: Constraints are like ICC playing-condition limits — a bowler's over is capped at six legal balls (a length constraint), and a team sheet must list exactly eleven players (a not-null requirement).
Prefer nullable=False on any column your application logic assumes is always present — catching missing data at the database level with a NOT NULL constraint is far more reliable than hoping every code path remembers to set it.
Relationships Between Models
A one-to-many relationship, like a user having many posts, is expressed with a foreign key on the 'many' side — user_id = db.Column(db.Integer, db.ForeignKey('users.id')) on Post — paired with db.relationship('Post', backref='author', lazy=True) on User, which gives you both user.posts (the list of posts) and post.author (the owning user) without writing any join manually. Many-to-many relationships use an intermediate association table passed to secondary= on db.relationship(), and for relationships needing extra columns on the join itself (like a timestamp on a follow relationship), you model the association as its own full class instead of a bare table.
Cricket analogy: It is like the relationship between a franchise and its players in the IPL auction — one franchise (the 'one') owns many players (the 'many'), and each player record carries a foreign key back to their franchise.
Using backref creates the reverse relationship implicitly and can obscure where it's defined when models grow large; many teams prefer explicit back_populates on both sides once a project scales past a handful of models, since it makes the bidirectional link visible in both class definitions.
- Models inherit from
db.Model; eachdb.Column()attribute maps to a table column. - Every model needs a primary key, conventionally an auto-incrementing
idcolumn. - Common types include
Integer,String(n),Text,Boolean,DateTime, andFloat. - Use
nullable=Falseandunique=Trueto enforce constraints at the database level. - One-to-many relationships pair a
ForeignKeycolumn withdb.relationship()andbackref. - Many-to-many relationships use an association table via
secondary=, or a full class if the join needs extra columns. Stringenforces a length cap at the database;Textis unbounded and suited to long content.
Practice what you learned
1. What determines a Flask-SQLAlchemy model's default table name?
2. Which column definition enforces that a field cannot be empty at the database level?
3. How is a one-to-many relationship typically expressed in Flask-SQLAlchemy?
4. When should you model an association as its own full class instead of using secondary= for a many-to-many relationship?
5. What is the key difference between db.String(80) and db.Text as column types?
Was this page helpful?
You May Also Like
Flask-SQLAlchemy Basics
Learn how Flask-SQLAlchemy wires the SQLAlchemy ORM into a Flask app, from configuration to sessions.
Querying with Flask-SQLAlchemy
Master filtering, joining, and optimizing database queries using Flask-SQLAlchemy's query interface.
Database Migrations with Flask-Migrate
Manage evolving database schemas safely using Flask-Migrate's Alembic-backed migration workflow.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics