100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Defining Models in Flask

Learn how to declare Flask-SQLAlchemy models with columns, types, constraints, and relationships.

Data & PersistenceBeginner9 min readJul 10, 2026
Analogies

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.

python
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; each db.Column() attribute maps to a table column.
  • Every model needs a primary key, conventionally an auto-incrementing id column.
  • Common types include Integer, String(n), Text, Boolean, DateTime, and Float.
  • Use nullable=False and unique=True to enforce constraints at the database level.
  • One-to-many relationships pair a ForeignKey column with db.relationship() and backref.
  • Many-to-many relationships use an association table via secondary=, or a full class if the join needs extra columns.
  • String enforces a length cap at the database; Text is unbounded and suited to long content.

Practice what you learned

Was this page helpful?

Topics covered

#Python#FlaskStudyNotes#WebDevelopment#DefiningModelsInFlask#Defining#Models#Flask#Declaring#StudyNotes#SkillVeris