SQLAlchemy入门

SQLAlchemy是Python世界中最普遍应用的ORM工具之一,它采取了相似于Java里Hibernate的数据映照模型,而不是其他ORM框架采取的Active Record模型。

SQLAlchemy分为两个部份,一个是最常常使用的ORM对象映照,另外一个是核心的SQL expression。第一个很好懂得,纯洁的ORM,后面这个不是ORM,而是DBAPI的封装,通过一些sql表达式来避免了直接写sql。应用SQLAlchemy则可以分为三种方法。

  • 应用ORM避免直接书写sql
  • 应用raw sql直接书写sql
  • 应用sql expression,通过SQLAlchemy的办法写sql表达式

安装

最简略的方法是通过pip安装

1
pip install SQLAlchemy

一般来说我们要对某个底层数据库须要安装相应的驱动,比如我应用了mysql,那末须要安装python的mysql驱动,有很多种选择,这里我选择了MySQLdb/MySQL-Python,这也是SQLAlchemy默许的。

在centos上面安装MySQL-Python

1
2
yum install mysql-devel
pip install MySQL-python

注意:MySQLdb仅仅支撑python2,如果要支撑python3,安装PyMySQL:

1
pip install PyMySQL

这里我应用python3.6版原来测试

定义映照

这里我应用两个表来解释,一个用户表users,一个电子邮件表addresses,二者一对多的关系。我们先定义这两个映照:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

Base = declarative_base()

class Address(Base):
"""电子邮件表"""
__tablename__ = "addresses"

id = Column(Integer, primary_key=True)
email_address = Column(String(30), nullable=False)
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="addresses")

def __repr__(self):
return "<Address(email_address="{}")>".format(self.email_address)

class User(Base):
"""用户表"""
__tablename__ = "users"

id = Column(Integer, primary_key=True)
name = Column(String(10))
fullname = Column(String(20))
password = Column(String(20))

addresses = relationship("Address", order_by=Address.id, back_populates="user")

def __repr__(self):
return "<User(name="{}", fullname="{}", password="{}")>".format(
self.name, self.fullname, self.password)

衔接到数据库

通过create_engine()可以衔接数据库,我应用的是PyMySQL,另外先要提早创立test这个测试数据库:

1
2
3
4
5
6
7
8
from sqlalchemy import create_engine

# 下面是MySQLdb/MySQL-Python默许写法
# engine = create_engine("mysql://root:[email protected]:3306/test", echo=True)

# 这里我应用的是PyMySQL
# echo=True是开启调试,这样当我们履行文件的时候会提醒相应的文字
engine = create_engine("mysql+pymysql://root:[email protected]:3306/test", echo=True)

现在我们只是定义了表映照,而数据库里面是没有真实表的,这里我们应用Base类的metadata来帮我们主动创立表:

1
Base.metadata.create_all(engine)

现在数据库里面已有我们的两个表了。下面我们对这两个表进行惯例操作

增删改查

对数据库的操作必需先创立一个session,增删改查操作都有这个session负责,首先我们先创立一个session工厂类,由它来负责后续的session创立

1
2
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)

添加用户

1
2
3
4
5
6
7
8
9
10
session = Session()  # 先应用工程类来创立一个session
ed_user = User(name="ed", fullname="Ed Jones", password="edspassword")
session.add(ed_user)
# 同时创立多个
session.add_all([
User(name="wendy", fullname="Wendy Williams", password="foobar"),
User(name="mary", fullname="Mary Contrary", password="xxg527"),
User(name="fred", fullname="Fred Flinstone", password="blah")])
# 提交事务
session.commit()

查询

在session上面调用query()办法会创立一个Query对象

1
2
3
4
5
6
7
8
9
for user in session.query(User).order_by(User.id):
print(user.name, user.fullname)

# 应用filter_by过滤
for name in session.query(User.name).filter_by(fullname="Ed Jones"):
print(name)
# 应用sqlalchemy的SQL表达式语法过滤,可以应用python语句
for name in session.query(User.name).filter(User.fullname=="Ed Jones"):
print(name)

删除

1
2
session.delete(ed_user)
session.query(User).filter_by(name="ed").count()

一对多的关系映照

sqlalchemy应用ForeignKey来指明一对多的关系,比如一个用户可有多个邮件地址,而一个邮件地址只属于一个用户。那末就是典范的一对多或多对一关系。

Address类中,我们定义外键,还有对应所属的user对象

1
2
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="addresses")

而在User类中,我们定义addresses属性

1
addresses = relationship("Address", order_by=Address.id, back_populates="user")

注意两个类中都通过relationship()办法指明相互关系。

通过几个例子来操作一对多的关系映照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 先添加一个用户,并且给这个用户增长两个邮件地址
jack = User(name="jack", fullname="Jack Bean", password="gjffdd")
jack.addresses = [Address(email_address="[email protected]"),
Address(email_address="[email protected]")]
session.add(jack)
session.commit()

# 查询
jack = session.query(User).filter_by(name="jack").one()
# 只有在调用jack.addresses时才会调用查询邮件地址的SQL,这个是典范的懒加载模式
jack.addresses

# join查询
session.query(User).join(Address).filter(Address.email_address=="[email protected]").all()

有时候我们不想应用懒加载,而是要强迫一次性加载某个关联数据,那末可以应用subqueryloadjoinedload

1
2
3
4
5
6
from sqlalchemy.orm import subqueryload
jack = session.query(User).options(subqueryload(User.addresses)).filter_by(name="jack").one()

# 推举应用下面这类计划
from sqlalchemy.orm import joinedload
jack = session.query(User).options(joinedload(User.addresses)).filter_by(name="jack").one()

本篇文章只是对sqlalchemy的入门篇,更多细节请参考官方教程