SQLAlchemy ORM
目录
使用SQLAlchemy ORM定义模式
1 使用ORM类定义表
要求:
- 继承自declarative_base对象
- 包含__tablename__,这是数据库使用的表名
- 包含一个或多个属性,它们都是column对象
- 确保一个或多个属性组成主键
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, Numeric, String, Boolean
Base = declarative_base() # 创建declarative_base类的一个实例
class Cookie(Base): # 继承base
__tablename__ = 'cookies' # 定义表名
cookie_id = Column(Integer(), primary_key=True) # 定义一个属性,并将其设置为主键
cookie_name = Column(String(50), index=True)
cookie_recipe_url = Column(String(255))
cookie_sku = Column(String(55))
quantity = Column(Integer())
unit_cost = Column(Numeric(12, 2))
键、约束、索引
在表构造函数中定义键和约束
class SomeDataClass(Base):
__tablename__ = 'somedatatable'
__table_args__ = (ForeignKeyConstraint(['id'], ['other_table.id']),CheckConstraint(unit_cost >= 0.00',name='unit_cost_positive'))
2 关系
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
#
class Order(Base):
__tablename__ = 'orders'
order_id = Column(Integer(), primary_key=True)
user_id = Column(Integer(), ForeignKey('users.user_id')) # 定义一个外键
shipped = Column(Boolean(), default=False)
user = relationship("User", backref=backref('orders', order_by=id)) # 建立一个一对多关系
# 一对一关系
class LineItems(Base):
__tablename__ = 'line_items'
line_items_id = Column(Integer(), primary_key=True)
order_id = Column(Integer(), ForeignKey('orders.order_id'))
cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))
quantity = Column(Integer())
extended_cost = Column(Numeric(12, 2))
order = relationship("Order", backref=backref('line_items', order_by=line_items_id))
cookie = relationship("Cookie", uselist=False)
3 模式持久化
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:123@127.0.0.1:3306/core?charset=utf8')
Base.metadata.create_all(engine)
使用SQLAlchemy ORM处理数据
1 会话
会话是SQLAlchemy ORM和数据库交互的方式。
sessionmaker类:确保整个应用程序中能够使用相同的参数创建会话。
# 创建一个可用来与数据库交互的session(不会自行连接到数据库)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('mysql+pymysql://root:123@127.0.0.1:3306/core?charset=utf8')
Session = sessionmaker(bind=engine)
session = Session()
# __repr__方法,方便查看和重建对象实例(不是必须的)
class Cookie(Base):
__tablename__ = 'cookies'
cookie_id = Column(Integer, primary_key=True)
cookie_name = Column(String(50), index=True)
cookie_recipe_url = Column(String(255))
cookie_sku = Column(String(55))
quantity = Column(Integer())
unit_cost = Column(Numeric(12, 2))
def __repr__(self):
return "Cookie(cookie_name='{self.cookie_name}', " "cookie_recipe_url='{self.cookie_recipe_url}', " "cookie_sku='{self.cookie_sku}', " "quantity={self.quantity}, " "unit_cost={self.unit_cost})".format(self=self)
2 插入数据
# 插入单条数据
cc_cookie = Cookie(cookie_name='chocolate chip',
cookie_recipe_url='http://some.aweso.me/cookie/recipe.html',
cookie_sku='CC01',
quantity=12,
unit_cost=0.50)
session.add(cc_cookie) # 将实例添加到会话
session.commit() # 提交会话
# 插入多条数据
dcc = Cookie(cookie_name='dark chocolate chip',
cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html',
cookie_sku='CC02',
quantity=1,
unit_cost=0.75)
mol = Cookie(cookie_name='molasses',
cookie_recipe_url='http://some.aweso.me/cookie/recipe_molasses.html',
cookie_sku='MOL01',
quantity=1,
unit_cost=0.80)
session.add(dcc)
session.add(mol)
session.flush()
# 批量插入多条数据
c1 = Cookie(cookie_name='peanut butter',
cookie_recipe_url='http://some.aweso.me/cookie/peanut.html',
cookie_sku='PB01',
quantity=24,
unit_cost=0.25)
c2 = Cookie(cookie_name='oatmeal raisin',
cookie_recipe_url='http://some.okay.me/cookie/raisin.html',
cookie_sku='EWW01',
quantity=100,
unit_cost=1.00)
session.bulk_save_objects([c1,c2])
session.commit()
速度很快,但是有缺陷:
1、关系设置和操作得不到遵守或触发
2、对象没有连接到会话
3、默认情况下,不获取主键
4、不会触发任何事件
3 查询数据
# 1 查询所有数据 all()
cookies = session.query(Cookie).all()
print(cookies)
# 2 first()第一个、one()查询所有行,如果返回不是单个结果,抛出异常、saclar() 返回第一个结果的第一个元素。无结果:None;多于一个,引发错误
# 3 控制查询中的列数
print(session.query(Cookie.cookie_name, Cookie.quantity).first())
# 4 排序
## 升序
for cookie in session.query(Cookie).order_by(Cookie.quantity):
print('{:3} - {}'.format(cookie.quantity, cookie.cookie_name))
## 降序
from sqlalchemy import desc
for cookie in session.query(Cookie).order_by(desc(Cookie.quantity)):
print('{:3} - {}'.format(cookie.quantity, cookie.cookie_name))
# 5 限制返回条数
##
query = session.query(Cookie).order_by(Cookie.quantity)[:2]
print([result.cookie_name for result in query])
##
query = session.query(Cookie).order_by(Cookie.quantity).limit(2)
print([result.cookie_name for result in query])
# 6 内置SQL函数和标签
## sum()
from sqlalchemy import func
inv_count = session.query(func.sum(Cookie.quantity)).scalar()
print(inv_count)
## count()
rec_count = session.query(func.count(Cookie.cookie_name)).first()
print(rec_count)
# 7 重命名统计列
rec_count = session.query(func.count(Cookie.cookie_name) .label('inventory_count')).first()
print(rec_count.keys())
print(rec_count.inventory_count)
# 8 过滤
## filter()
record = session.query(Cookie).filter(Cookie.cookie_name == 'chocolate chip').first()
print(record)
## filter_by()
record = session.query(Cookie).filter_by(cookie_name='chocolate chip').first()
print(record)
最佳实践
- 使用迭代而非all()方法获取记录
- 获取单条记录,使用first()
- 谨慎使用scalar()方法,如果查询返回的数据不只一行一列,会引发错误
4 更新数据
# 通过对象更新数据
query = session.query(Cookie)
cc_cookie = query.filter(Cookie.cookie_name == "chocolate chip").first()
cc_cookie.quantity = cc_cookie.quantity + 120
session.commit()
print(cc_cookie.quantity)
# 就地更新数据
query = session.query(Cookie)
query = query.filter(Cookie.cookie_name == "chocolate chip")
query.update({Cookie.quantity: Cookie.quantity - 20})
cc_cookie = query.first()
print(cc_cookie.quantity)
5 删除数据
query = session.query(Cookie)
query = query.filter(Cookie.cookie_name == "dark chocolate chip")
dcc_cookie = query.one()
session.delete(dcc_cookie)
session.commit()
dcc_cookie = query.first()
print(dcc_cookie)
# 直接删除,无需获取待删除对象
query = session.query(Cookie)
query = query.filter(Cookie.cookie_name == "molasses")
query.delete()
mol_cookie = query.first()
print(mol_cookie)
6 连接
# 使用连接查询多个表
query = session.query(Order.order_id, User.username, User.phone,
Cookie.cookie_name, LineItem.quantity,
LineItem.extended_cost)
query = query.join(User).join(LineItem).join(Cookie)
results = query.filter(User.username == 'cookiemon').all()
print(results)
# 使用外连接查询多个表
query = session.query(User.username, func.count(Order.order_id))
query = query.outerjoin(Order).group_by(User.username)
for row in query:
print(row)
# remote_side:自引用,形成多对一关系
class Employee(Base):
__tablename__ = 'employees'
id = Column(Integer(), primary_key=True)
manager_id = Column(Integer(), ForeignKey('employees.id'))
name = Column(String(255), nullable=False)
manager = relationship("Employee", backref=backref('reports'), remote_side=[id])
Base.metadata.create_all(engine)
7 分组
query = session.query(User.username, func.count(Order.order_id))
query = query.outerjoin(Order).group_by(User.username)
for row in query:
print(row)
8 链式调用(函数)
def get_orders_by_customer(cust_name, shipped=None, details=False):
query = session.query(Order.order_id, User.username, User.phone)
query = query.join(User)
if details:
query = query.add_columns(Cookie.cookie_name, LineItem.quantity,
LineItem.extended_cost)
query = query.join(LineItem).join(Cookie)
if shipped is not None:
query = query.filter(Order.shipped == shipped)
results = query.filter(User.username == cust_name).all()
return results
print(get_orders_by_customer('cakeeater'))
print(get_orders_by_customer('cakeeater', details=True))
print(get_orders_by_customer('cakeeater', shipped=True))
print(get_orders_by_customer('cakeeater', shipped=False))
print(get_orders_by_customer('cakeeater', shipped=False, details=True))
9 原始查询
from sqlalchemy import text
query = session.query(User).filter(text("username='cookiemon'"))
print(query.all())
理解会话和异常
1 SQLAlchemy会话
会话状态:
- transient(瞬时状态):实例不在会话中,也不在数据库中
- pending(挂起状态):实例由add()方法添加到会话中,但仍未刷新或提交
- persistent(持久化状态):会话中的对象在数据库中有对应的记录
- detached(脱管状态):实例不再与会话相连,但在数据库中仍然有一条记录
2 异常
1 MultipleResultsFound异常
使用.one()方法,返回多个结果,就会触发这个异常
from sqlalchemy.orm.exc import MultipleResultsFound
try:
results = session.query(Cookie).one()
except MultipleResultsFound as error:
print('We found too many cookies... is that even possible?')
2 DetachedInstanceError
当尝试访问某个实例的某个属性时,如果这个实例需要从数据库加载,但他目前又没有连接到数据库,就会发生DetachedInstanceError异常
3 事务
使用SQLALchemy ORM和自动映射进行反射
1 使用自动映射反射数据库
# 使用automap_base创建base对象
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine
Base = automap_base()
# 初始化引擎
from sqlalchemy import create_engine
# engine = create_engine('sqlite:///Chinook_Sqlite.sqlite')
engine = create_engine('mysql+pymysql://root:123@127.0.0.1:3306/core?charset=utf8')
# base.prepare()方法,将扫描我们刚刚创建的引擎上的所有可用内容,并反射它所能反射的所有内容
Base.prepare(engine, reflect=True)
Base.classes.keys() # 获取数据库所有表
users = Base.classes.users # 将一个表的关系赋给users,创建引用
# 使用表
from sqlalchemy.orm import Session
session = Session(engine)
for artist in session.query(users).limit(10):
print(artist.username, artist.email_address)
2 反射关系
自动映射可用自动反射和创建多对一、一对多、多对多的关系