SQLAlchemy
一、CRM介绍
数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含id
和name
的user
表:
[ ('1', 'Michael'), ('2', 'Bob'), ('3', 'Adam') ]
Python的DB-API返回的数据结构就是像上面这样表示的。
但是用tuple表示一行很难看出表的结构。如果把一个tuple用class实例来表示,就可以更容易地看出表的结构来:
class User(object): def __init__(self, id, name): self.id = id self.name = name [ User('1', 'Michael'), User('2', 'Bob'), User('3', 'Adam') ]
这就是传说中的ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上。
在Python中,最有名的ORM框架是SQLAlchemy。
二、SqlAlchemy基本操作
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<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...] 更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
安装SQLAlchemy、mysql-connector
pip install SQLAlchemy
pip install --egg mysql-connector
1、使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine # 初始化数据库连接 engine = create_engine("mysql+mysqlconnector://root:123,.abc@127.0.0.1:3306/s12day9", max_overflow=5) engine.execute( "INSERT INTO test (id, name) VALUES ('2', 'Rambo')" ) #engine.execute( # "INSERT INTO test (a, b) VALUES (%s, %s)", # ((555, "v1"),(666, "v1"),) #) #engine.execute( # "INSERT INTO test (a, b) VALUES (%(id)s, %(name)s)", # id=999, name="v1" #) result = engine.execute('select * from test') result.fetchall()
2、使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 进行数据库操作。Engine使用Schema Type创建一个特定的结构对象,之后通过SQL Expression Language将该对象转换成SQL语句,然后通过 ConnectionPooling 连接数据库,再然后通过 Dialect 执行SQL,并获取结果。
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey metadata = MetaData() user = Table('users', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)), ) color = Table('color', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)), ) engine = create_engine("mysql+mysqlconnector://root:123,.abc@localhost:3306/s12day9", max_overflow=5) metadata.create_all(engine)
增删改查
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey, select
metadata = MetaData()
user = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(20)),
)
color = Table('color', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(20)),
)
engine = create_engine("mysql+mysqlconnector://root:123,.abc@127.0.0.1:3306/s12day9", max_overflow=5)
conn = engine.connect()
# 创建SQL语句,INSERT INTO "user" (id, name) VALUES (:id, :name)
#conn.execute(user.insert(),{'id':1,'name':'flash'})
#conn.close()
# 插入数据的另一种方法
#sql = user.insert().values(id=2, name='Rambo')
#conn.execute(sql)
#conn.close()
# 删除id大于1的记录
#sql = user.delete().where(user.c.id > 1)
#conn.execute(sql)
#conn.close()
sql = user.update().values(fullname=user.c.name)
sql = user.update().where(user.c.name == 'jack').values(name='ed')
sql = select([user, ])
sql = select([user.c.id, ])
sql = select([user.c.name, color.c.name]).where(user.c.id==color.c.id)
sql = select([user.c.name]).order_by(user.c.name)
sql = select([user]).group_by(user.c.name)
result = conn.execute(sql)
print(result.fetchall())
conn.close()
完整的例子:
#!/usr/bin/env python # -*- coding:utf-8 -*-
# 导入 from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker
# 创建对象的基类 Base = declarative_base() # 初始化数据库连接, echo为False表示不显示消息,为True则显示。 engine = create_engine("mysql+mysqlconnector://root:123,.abc@localhost:3306/s12day9",ec ho=False) class Host(Base):
# 表的名字 __tablename__ = 'hosts'
# 表的结构 id = Column(Integer,primary_key=True,autoincrement=True) hostname = Column(String(64),unique=True,nullable=False) ip_addr = Column(String(128),unique=True,nullable=False) port = Column(Integer,default=22)
# 生成一个SqlORM 基类
Base.metadata.create_all(engine)
以上代码完成SQLAlchemy的初始化和具体每个表的class定义。如果有多个表,就继续定义其他class,例如app:
class app(Base): __tablename__ = 'school' id = ... name = ...
创建数据库会话,往数据库添加记录:
if __name__ == '__main__': # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 SessionCls = sessionmaker(bind=engine) session = SessionCls() #h1 = Host(hostname='localhost',ip_addr='127.0.0.1') h2 = Host(hostname='ubuntu',ip_addr='192.168.2.243',port=20000) h3 = Host(hostname='ubuntu2',ip_addr='192.168.2.244',port=20000) session.add(h3) #将h3添加到session #session.add_all( [h1,h2]) #批量添加 h2.hostname = 'ubuntu_test' #只要没提交,此时修改也没问题
#session.rollback()
# 创建Query查询,filter是where条件,最后调用all()返回所有行,调用one()则返回唯一行
# res=session.query(Host).filter(Host.hostname.in_(['ubuntu2','localhost'])).all()
# print(res)
### 先查询后修改
#res = session.query(Host).filter(Host.hostname=="localhost").first()
#res.hostname = "test server"
### 先查询后删除
#res = session.query(Host).filter(Host.hostname=="test server").first()
#session.delete(res)
session.commit() #提交
更多内容详见:
http://www.jianshu.com/p/e6bba189fcbd
http://docs.sqlalchemy.org/en/latest/core/expression_api.html
3、使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有组件对数据进行操作。根据类创建对象,对象转换成SQL,执行SQL。
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine engine = create_engine("mysql+mysqlconnector://root:123,.abc@127.0.0.1:3306/s12day9", max_overflow=5) Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) # 寻找Base的所有子类,按照子类的结构在数据库中生成对应的数据表信息 # Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() # ########## 增 ########## # u = User(id=2, name='sb') # session.add(u) # session.add_all([ # User(id=3, name='sb'), # User(id=4, name='sb') # ]) # session.commit() # ########## 删除 ########## # session.query(User).filter(User.id > 2).delete() # session.commit() # ########## 修改 ########## # session.query(User).filter(User.id > 2).update({'cluster_id' : 0}) # session.commit() # ########## 查 ########## # ret = session.query(User).filter_by(name='sb').first() # ret = session.query(User).filter_by(name='sb').all() # print ret # ret = session.query(User).filter(User.name.in_(['sb','bb'])).all() # print ret # ret = session.query(User.name.label('name_label')).all() # print ret,type(ret) # ret = session.query(User).order_by(User.id).all() # print ret # ret = session.query(User).order_by(User.id)[1:3] # print ret # session.commit()
三、外键关联
由上面的例子可以看出,ORM就是把数据库表的行与相应的对象建立关联,互相转换。
由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地,ORM框架也可以提供两个对象之间的一对多、多对多等功能。
例如,如果一个Group拥有多个Host,就可以定义一对多(一个组可以对应多个主机,但一个主机只能对应一个组)关系如下:
class Host(Base): __tablename__ = 'hosts' id = Column(Integer,primary_key=True,autoincrement=True) hostname = Column(String(64),unique=True,nullable=False) ip_addr = Column(String(128),unique=True,nullable=False) port = Column(Integer,default=22) 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) # host_id = Column(Integer,ForeignKey('host.id')) # 如果host_id写在Group里,就相当于一对一了,一个组对应一台主机,所以一定要写在host里面
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 from sqlalchemy import create_engine 5 from sqlalchemy import Table, Column, Integer, String, ForeignKey 6 from sqlalchemy.orm import relationship 7 from sqlalchemy.ext.declarative import declarative_base 8 from sqlalchemy.orm import sessionmaker 9 10 Base = declarative_base() 11 12 engine = create_engine("mysql+mysqlconnector://root:123,.abc@localhost:3306/s12day9",echo=True) 13 14 15 class Host(Base): 16 __tablename__ = 'hosts' 17 id = Column(Integer,primary_key=True,autoincrement=True) 18 hostname = Column(String(64),unique=True,nullable=False) 19 ip_addr = Column(String(128),unique=True,nullable=False) 20 port = Column(Integer,default=22) 21 group_id = Column(Integer,ForeignKey('group.id')) 22 23 class Group(Base): 24 __tablename__ = 'group' 25 id = Column(Integer,primary_key=True) 26 name = Column(String(64),unique=True,nullable=False) 27 # host_id = Column(Integer,ForeignKey('host.id')) 28 29 Base.metadata.create_all(engine) #创建所有表结构 30 31 if __name__ == '__main__': 32 SessionCls = sessionmaker(bind=engine) 33 session = SessionCls() 34 session.commit()
2016-06-28 23:27:58,944 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2016-06-28 23:27:58,944 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,951 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2016-06-28 23:27:58,951 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,957 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2016-06-28 23:27:58,957 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,961 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2016-06-28 23:27:58,962 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,964 INFO sqlalchemy.engine.base.Engine DESCRIBE `hosts`
2016-06-28 23:27:58,964 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,965 INFO sqlalchemy.engine.base.Engine ROLLBACK
2016-06-28 23:27:58,967 INFO sqlalchemy.engine.base.Engine DESCRIBE `group`
2016-06-28 23:27:58,967 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:58,969 INFO sqlalchemy.engine.base.Engine ROLLBACK
2016-06-28 23:27:58,972 INFO sqlalchemy.engine.base.Engine
CREATE TABLE `group` (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
PRIMARY KEY (id),
UNIQUE (name)
)
2016-06-28 23:27:58,972 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:59,018 INFO sqlalchemy.engine.base.Engine COMMIT
2016-06-28 23:27:59,019 INFO sqlalchemy.engine.base.Engine
CREATE TABLE hosts (
id INTEGER NOT NULL AUTO_INCREMENT,
hostname VARCHAR(64) NOT NULL,
ip_addr VARCHAR(128) NOT NULL,
port INTEGER,
group_id INTEGER,
PRIMARY KEY (id),
UNIQUE (hostname),
UNIQUE (ip_addr),
FOREIGN KEY(group_id) REFERENCES `group` (id)
)
2016-06-28 23:27:59,019 INFO sqlalchemy.engine.base.Engine {}
2016-06-28 23:27:59,064 INFO sqlalchemy.engine.base.Engine COMMIT
注:SQLAlchemy无法修改表结构(如果需要可以使用SQLAlchemy开发者开源的另外一个软件Alembic来完成),所以这里先删除hosts表(测试环境)再重新创建。
更多功能参见文档,猛击这里下载PDF
总结:
ORM框架的作用就是把数据库表的一行记录与一个对象互相做自动转换。
正确使用ORM的前提是了解关系数据库的原理。