SQLAlchemy

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQLAlchemy相关的知识,希望对你有一定的参考价值。

一、CRM介绍

  数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含idnameuser表:

[
    (‘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:[email protected]: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,[email protected]: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,[email protected]: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,[email protected]: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,[email protected]: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,[email protected]: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,[email protected]: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的前提是了解关系数据库的原理。

 

以上是关于SQLAlchemy的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy的同步和异步的代码对比

SQLAlchemy的同步和异步的代码对比

SQLAlchemy的同步和异步的代码对比

SQLAlchemy:数据库创建代码小于数据库

python SQLAlchemy 缓存问题

python SQLAlchemy 缓存问题