Integrating SQLAlchemy into a Flask application provides a robust object-relational mapping (ORM) layer that simplifies database interactions. The Flask-SQLAlchemy extension bridges the gap between Flask's request context and SQLAlchemy's session management.
Environment Setup
Begin by installing the extension via the package manager:
pip install flask-sqlalchemy
Framwork Configuration
Initialize the ORM by linking it to your Flask application instance. Configure the database connection string using a standardized URI format that specifies the dialect, driver, credentials, and target schema.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
application = Flask(__name__)
# Format: dialect+driver://user:pass@host:port/dbname?params
application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app_data.db'
application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
orm = SQLAlchemy(application)
Model Declaration
Models are defined as subclasses of orm.Model. Each class attribute represents a database column. The table name is automatically derived from the class name in lowercase.
class Account(orm.Model):
identifier = orm.Column(orm.Integer, primary_key=True, autoincrement=True)
handle = orm.Column(orm.String(64), unique=True, nullable=False)
contact_email = orm.Column(orm.String(128), unique=True)
def __init__(self, handle, email):
self.handle = handle
self.contact_email = email
def persist(self):
orm.session.add(self)
orm.session.commit()
def __repr__(self):
return f'<Account {self.handle}>'
Schema Generation and Basic Operations
Generate the underlying database schema by calling create_all() on the ORM instance. This operation is idempotent and will only create missing tables.
with application.app_context():
orm.create_all()
To store records, instantiate the model and add it to the session before committing. Querying utilizes the .query attribute or modern session-based syntax.
# Record insertion
new_user = Account(handle='sys_admin', email='admin@domain.local')
new_user.persist()
# Data retrieval
all_accounts = Account.query.all()
admin_record = Account.query.filter_by(handle='sys_admin').first()
When storing non-ASCII characters, ensure the connection and string handling support UTF-8 encoding natively.
Establishing Relationships
Relational databases rely on foreign keys and association tables to connect entities. Flask-SQLAlchemy provides declarative syntax for these mappings.
One-to-Many Mapping
A typical example involves a Department housing multiple Engineer records. The foreign key resides on the "many" side, while the relationship object is placed on the "one" side.
class Department(orm.Model):
dept_id = orm.Column(orm.Integer, primary_key=True)
division_name = orm.Column(orm.String(100))
def __init__(self, name):
self.division_name = name
def __repr__(self):
return f'<Dept {self.division_name}>'
class Engineer(orm.Model):
eng_id = orm.Column(orm.Integer, primary_key=True)
full_name = orm.Column(orm.String(80))
specialty = orm.Column(orm.String(100))
# Foreign key linking back to Department
dept_ref = orm.Column(orm.Integer, orm.ForeignKey('department.dept_id'))
# Relationship object enabling bidirectional access
assigned_dept = orm.relationship('Department', backref=orm.backref('staff_members', lazy='dynamic'))
def __init__(self, name, spec, department):
self.full_name = name
self.specialty = spec
self.assigned_dept = department
Querying traverses both directions seamlessly:
dev_team = Department('Software Development')
orm.session.add(dev_team)
orm.session.commit()
# Access related engineers
print(dev_team.staff_members.all()) # Returns dynamic query object, .all() executes it
# Reverse lookup
new_dev = Engineer('Alice Smith', 'Backend', dev_team)
new_dev.persist()
print(new_dev.assigned_dept) # Yields the parent Department object
Many-to-Many Mapping
Complex associations require an explicit junction table. This table holds foreign keys pointing to both participating models.
enrollment_mapping = orm.Table(
'class_enrollments',
orm.Column('student_id', orm.Integer, orm.ForeignKey('student.student_id')),
orm.Column('course_id', orm.Integer, orm.ForeignKey('course.course_id'))
)
class Student(orm.Model):
student_id = orm.Column(orm.Integer, primary_key=True)
full_name = orm.Column(orm.String(60))
enrolled_courses = orm.relationship('Course', secondary=enrollment_mapping,
backref=orm.backref('registered_students', lazy='dynamic'))
class Course(orm.Model):
course_id = orm.Column(orm.Integer, primary_key=True)
subject_code = orm.Column(orm.String(10), unique=True)
title = orm.Column(orm.String(100))
Populating this relationship involves appending model instances to the collection list before committing the session. Queries can traverse enrolled_courses or registered_students dynamically.
Hierarchical Self-Reference
Tree-like structures, such as directory hierarchies or nested categories, map efficiently using a self-referential foreign key.
class Node(orm.Model):
node_id = orm.Column(orm.Integer, primary_key=True)
label = orm.Column(orm.String(50))
parent_ref = orm.Column(orm.Integer, orm.ForeignKey('node.node_id'))
# Relationship defines parent/child linkage
parent_node = orm.relationship('Node', remote_side=[node_id],
backref=orm.backref('child_nodes', uselist=True))
def __init__(self, name, parent=None):
self.label = name
self.parent_node = parent
Navigating the hierarchy relies on the child_nodes and parent_node attributes. Setting a parent automatical establishes the reverse link, enabling recursive traverrsal or level-based filtering.