SQLAlchemy is a Python ORM framework built upon the DB API, translating classes and objects into SQL, executing it, and retrieving results. It can create and drop tables but not alter structures.
Install via pip:
pip3 install sqlalchemy
Core components:
- Engine: the framework's core
- Connection Pooling: manages database connections
- Dialect: selects the DB API for the databace
- Schema/Types: defines table structures and data types
- SQL Expression Language: provides SQL-like expressions
SQLAlchemy relies on third-party drivers like pymysql. The Dialect communicates with the chosen DB API based on configuration, for example:
MySQL-Python:
mysql+mysqldb://user:password@host[:port]/dbname
PyMySQL:
mysql+pymysql://user:password@host/dbname[?options]
MySQL-Connector:
mysql+mysqlconnector://user:password@host[:port]/dbname
cx_Oracle:
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
Defining Table Structures
Single Table
from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, UniqueConstraint, Index
from sqlalchemy.ext.declarative import declarative_base
import datetime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=False)
email = Column(String(32), unique=True)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
notes = Column(Text, nullable=True)
__table_args__ = (
UniqueConstraint('id', 'name', name='uix_id_name'),
Index('ix_id_name', 'name', 'email'),
)
def create_tables():
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/mydb?charset=utf8",
max_overflow=0, pool_size=5, pool_timeout=30, pool_recycle=-1
)
Base.metadata.create_all(engine)
def drop_tables():
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/mydb?charset=utf8",
max_overflow=0, pool_size=5, pool_timeout=30, pool_recycle=-1
)
Base.metadata.drop_all(engine)
if __name__ == '__main__':
drop_tables()
create_tables()
Multiple Tables with ForeignKey and Many-to-Many
from sqlalchemy import create_engine, Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
import datetime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True)
age = Column(Integer, default=18)
email = Column(String(32), unique=True)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
extra = Column(Text, nullable=True)
class Host(Base):
__tablename__ = 'hosts'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
name = Column(String(50), default='Basketball')
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=True)
hobby_id = Column(Integer, ForeignKey("hobby.id"))
hobby = relationship("Hobby", backref='persons')
class ServerGroup(Base):
__tablename__ = 'server_group'
id = Column(Integer, primary_key=True, autoincrement=True)
server_id = Column(Integer, ForeignKey('server.id'))
group_id = Column(Integer, ForeignKey('group.id'))
class Group(Base):
__tablename__ = 'group'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
servers = relationship('Server', secondary='server_group', backref='groups')
class Server(Base):
__tablename__ = 'server'
id = Column(Integer, primary_key=True, autoincrement=True)
hostname = Column(String(64), unique=True, nullable=False)
def init_db():
engine = create_engine(
"mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8",
max_overflow=0, pool_size=5, pool_timeout=30, pool_recycle=-1
)
Base.metadata.create_all(engine)
def drop_db():
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8",
max_overflow=0, pool_size=5, pool_timeout=30, pool_recycle=-1
)
Base.metadata.drop_all(engine)
if __name__ == '__main__':
drop_db()
init_db()
Connecting to the Database
Basic Connection
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
import models
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# Perform ORM operations
user = models.User(name="derek", email='derek@163.com')
session.add(user)
session.commit()
# Return connection to pool
session.close()
Thread-Safe Connection with scoped_session
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import create_engine
import models
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# Perform ORM operations
user = models.User(name="jack", email='jack@163.com')
session.add(user)
session.commit()
session.close()
Basic ORM Operations
Adding Records
# Single addition
user = models.User(name="jack", email='jack@163.com')
session.add(user)
session.commit()
# Batch addition
session.add_all([
models.User(name="jack1", email='jack1@163.com'),
models.User(name="jack2", email='jack2@163.com'),
])
session.commit()
Querying Data
user_list = session.query(models.User).all()
for user in user_list:
print(user.id, user.name, user.email)
# Filtering
user_list = session.query(models.User).filter(models.User.id > 2).all()
Deleting Records
session.query(models.User).filter(models.User.id > 4).delete()
session.commit()
Updating Records
session.query(models.User).filter(models.User.id == 4).update({'name': 'Tom'})
session.query(models.User).filter(models.User.id == 1).update({'name': models.User.name + "099"}, synchronize_session=False)
session.query(models.User).filter(models.User.id == 3).update({"age": models.User.age + 1}, synchronize_session="evaluate")
session.commit()
Advanced Query Techniques
# Conditions
ret = session.query(User).filter_by(name='alex').all()
ret = session.query(User).filter(User.id > 1, User.name == 'eric').all()
ret = session.query(User).filter(User.id.between(1, 3), User.name == 'eric').all()
ret = session.query(User).filter(User.id.in_([1,3,4])).all()
ret = session.query(User).filter(~User.id.in_([1,3,4])).all()
# And/Or
from sqlalchemy import and_, or_
ret = session.query(User).filter(and_(User.id > 3, User.name == 'eric')).all()
ret = session.query(User).filter(or_(User.id < 2, User.name == 'eric')).all()
# Wildcard
ret = session.query(User).filter(User.name.like('e%')).all()
ret = session.query(User).filter(~User.name.like('e%')).all()
# Limiting
ret = session.query(User)[1:2]
# Sorting
ret = session.query(User).order_by(User.name.desc()).all()
ret = session.query(User).order_by(User.name.desc(), User.id.asc()).all()
# Grouping
from sqlalchemy.sql import func
ret = session.query(func.max(User.id), func.sum(User.id), func.min(User.id)).group_by(User.name).all()
ret = session.query(func.max(User.id), func.sum(User.id), func.min(User.id)).group_by(User.name).having(func.min(User.id) > 2).all()
# Joins
ret = session.query(User, Hobby).filter(User.id == Hobby.id).all()
ret = session.query(Person).join(Hobby).all()
ret = session.query(Person).join(Hobby, isouter=True).all() # Left join
# Set Operations
q1 = session.query(User.name).filter(User.id > 2)
q2 = session.query(Hobby.name).filter(Hobby.id < 2)
ret = q1.union(q2).all()
ret = q1.union_all(q2).all()
Using Raw SQL
from sqlalchemy import text
cursor = session.execute('select * from users')
result = cursor.fetchall()
cursor = session.execute('insert into users(name) values(:value)', params={"value":'example'})
session.commit()
print(cursor.lastrowid)
session.close()
Cross-Tible Relationships
Adding Related Data
session.add_all([
models.Hobby(name="Reading"),
models.Hobby(name="Swimming")
])
session.commit()
session.add_all([
models.Person(name="Alice", hobby_id=2),
models.Person(name="Bob", hobby_id=2),
models.Person(name="Charlie", hobby_id=1),
])
session.commit()
Querying from One to Many
person = session.query(models.Person).filter(models.Person.id == 1).first()
print(person.hobby_id)
Querrying from Many to One
hobby = session.query(models.Hobby).filter(models.Hobby.id == 1).first()
for person in hobby.persons:
print(person.name)
Inserting with Relationships
hobby = models.Hobby(name='Gaming')
hobby.persons = [models.Person(name='David'), models.Person(name='Emma')]
session.add(hobby)
session.commit()