简介
SQLAlchemy 实际上它分为两部分——底层的 Core 和上层的传统 ORM。
- Core 是SQLAlchemy作为“数据库工具包”的基础架构。该库提供了用于管理与数据库的连接、与数据库查询和结果交互以及SQL语句的编程构造的工具。 ORM 以核心为基础提供可选 对象关系映射 能力。
- ORM提供了一个附加的配置层,允许用户定义的Python类 映射 以及一种称为 会话 . 然后,它扩展了核心级别的SQL表达式语言,允许按照用户定义的对象组合和调用SQL查询。
如何选择?
- 虽然你使用的框架中已经内置了ORM,但是希望添加更强大的报表功能,请选用Core。
- 如果你想在一个一模式为中心的视图中查看数据(用户类似于SQL),请使用Core。
- 如果你的数据不需要业务对象,请使用Core。
- 如果你要把数据看作业务对象,请使用ORM。
- 如果你想快速创建原型,请使用ORM。
- 如果你需要同事使用业务对象和其他与问题域无关的数据,请组合使用Core和ORM。
创建链接
from sqlalchemy import create_engine
engine = create_engine(
"mysql://user:password@localhost:3306/dbname",
echo=True, # echo 设为 true 会打印出实际执行的 sql,调试的时候更方便
future=True, # 使用 2.0API,向后兼容
pool_size=5, # 连接池的大小默认为 5 个,设置为 0 时表示连接无限制
pool_recycle: 3600, # 设置时间以限制数据库多久没连接自动断开。
)
Core的使用方式
使用text、select、insert、update和delete等构造SQL,再由execute执行。实践中推荐SQL与SQL执行分开,尤其是较为复杂的SQL。
使用纯SQL
- 无参数
stmt=text("select * from db_name")
with engine.connect() as conn:
result = conn.execute(stmt)
print(result.all())
- 有参数
- 方式一,多参数
with engine.connect() as conn:
stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)")
params=[{"x": 1, "y": 1}, {"x": 2, "y": 4}]
conn.execute(stmt,params)
conn.commit()
- 方式二,与语句绑定
with engine.connect() as conn:
stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)").bindparams([{"x": 1, "y": 1}, {"x": 2, "y": 4}])
conn.execute(stmt)
conn.commit()
方式风格
- 边做边做
with engine.connect() as conn:
stmt=...
conn.execute(stmt)
- 开始一次(推荐)
此方法将同时管理 Connection并在事务结束时使用COMMIT将事务内部的所有内容括起来,假设块成功,或者在异常引发时回滚
with engine.begin() as conn:
stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)").bindparams([{"x": 1, "y": 1}, {"x": 2, "y": 4}])
conn.execute(stmt)
获取与使用查询结果
execute()函数的返回值是一热ResultProxy对象,它允许使用索引、名称或Column对象进行访问。
- 元组赋值,即解包,在接收到变量时将变量按位置分配给每一行
result = conn.execute(text("select x, y from some_table"))
for x, y in result:
.....
- 属性名称,元组具有与每个列的名称相匹配的动态属性名。这些名称通常是SQL语句为每行中的列指定的名称
result = conn.execute(text("select x, y from some_table"))
for row in result:
y = row.y
# illustrate use with Python f-strings
print(f"Row: {row.x} {row.y}")
- 属性名称与模型结合,即结果类型映射,个人更喜欢这种方式,编程效率会更高
result = conn.execute(text("select x, y from some_table"))
result:[TableModel]=result.fetchall()
for row in result:
y = row.y
# illustrate use with Python f-strings
print(f"Row: {row.x} {row.y}")
- 整数索引,不推荐
使用ORM会话执行Session
使用ORM时,基本的事务/数据库交互对象称为 Session
>>> from sqlalchemy.orm import Session
>>> stmt = text("SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y").bindparams(y=6)
>>> with Session(engine) as session:
... result = session.execute(stmt)
... for row in result:
... print(f"x: {row.x} y: {row.y}")
映射Python类
在 1.4 版更改: 声明性映射和经典映射现在被称为“声明性”和“命令式”映射,并且在内部是统一的
- 命令式(Table类)
student = Table('student', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50), ),
Column('age', Integer),
Column('address', String(10)),
)
- 声明式(模型类)
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base() # 生成模型类的基类
class User(Base): # 模型类必须通过继承基类来获得metadata
__tablename__ = 'users' # 声明需要映射的表名
id = Column(Integer,primary_key=True)
name = Column(String(20),nullable=False)
使用类模型
实践中使声明式会更方便些
select_stmt = select(User.id, User.name).where(User.id == 41865)
insert_stmt = insert(User).values(name='name1')
with engine.begin() as beg:
beg.execute(select_stmt)
beg.execute(insert_stmt)
ORM 使用方式
Session是对transcation的封装,最重要的功能是实现原子操作。要完成数据库查询,就需要建立与数据库的连接。这就需要用到Engine对象。一个Engine可能是关联一个Session对象,也可能关联一个数据库表。一旦任务完成 session 会将数据库 connection 交还给 pool。
建立session链接
future=True 使用2.0API
from sqlalchemy.orm import sessionmaker
DBSession = sessionmaker(bind=engine,autocommit=False, autoflush=False,
future=True)
with DBSession() as sess:
sess.execute()
with DBSession.begin() as sess:
sess.execute()
session的四种状态
ORM模型很方便地将数据库中的一条条记录转变成了python中的一个个对象,有时候我们会想当然地把两者完全等同起来,但是不要忘了,两者之间还必须有session这个中间的桥梁。因为有session在中间做控制,所以必须注目对象和记录之间一个状态上的差别。一般而言,一个数据的对象可以有四种不同的和session关联的状态。
from sqlalchemy.orm import sessionmaker
DBSession = sessionmaker(bind=engine)
session = DBSession() #创建session对象
frank = Person(name='Frank') #数据对象得到创建,此时为Transient状态
session.add(frank) #数据对象被关联到session上,此时为Pending状态
session.commit() #数据对象被推到数据库中,此时为Persistent状态
session.close() #关闭session对象
print (frank.name) #此时会报错DetachedInstanceError,因为此时是Detached状态。
new_session = DBSession()
print (new_session.query(Person).get(1).name) #可以查询到数据
new_session.close()
增删改查
- 增
insert_stmt = insert(User).values(name='name1')
with DBSession() as sess:
sess.execute(insert_stmt)
sess.commit()
insert_stmt2 = insert(User)
with DBSession() as sess:
sess.execute(insert_stmt2,{'name':'name1'})
sess.commit()
with DBSession() as sess:
sess.execute(insert_stmt2,[{'name':'name1'},{'name':'name2'}])
sess.commit()
with DBSession.begin() as sess:
sess.execute(insert_stmt2,[{'name':'name1'},{'name':'name2'}])
obj=User(name='name2')
with DBSession() as sess:
sess.add(obj)
sess.commit()
obj=User(name='name2')
obj2=User(name='name2')
with DBSession() as sess:
sess.add(obj)
sess.add(obj2)
# 或者 s.bulk_save_objects([obj,obj2])
sess.commit()
- 查、改和删
2.0 API 中不再使用 query,而是使用 select,update,delete 来操作数据。但query仍可使用
# session.query(User).get(42)
session.get(User, 42)
# session.query(User).all()
session.execute(select(User)).scalars().fetchall()
# session.query(User).filter_by(name="some_user").first()
session.execute(select(User).filter_by(name="some_user")).fetchone()
# session.query(User).from_statememt(text("select * from users")).a..()
session.execute(select(User).from_statement(text("selct * from users"))).scalars().all()
# session.query(User).filter(User.name == "foo").update({"fullname": "FooBar"}, synchronize_session="evaluate")
session.execute(update(User).where(User.name == "foo").values(fullname="FooBar").execute_options(synchronize_session="evaluate"))
# synchronize_session 有三种选项: false, "fetch", "evaluate",默认是 evaluate
# false 表示完全不更新 Python 中的对象
# fetch 表示重新从数据库中加载一份对象
# evaluate 表示在更新数据库的同时,也尽量在 Python 中的对象上使用同样的操作
自动生成模型
sqlacodegen --outfile=models.py mysql://root:[email protected]:3306/test --tables teacher,student
sqlacodegen --outfile=models.py mysql://root:[email protected]:3306/test --tables teacher,student ----noclasses