python2.0_s12_day11_SqlAlchemy使用介绍


SqlAlchemy ORM
ORM的解释;
简单点:对象关系映射.
需求:我们写一个主机管理,把主机信息存在数据库,一开始我们编程不熟练的时候,执行命令时候要调用数据库,会把相应的SQL语句写到代码中,这种叫做hard coding(硬生生的代码),那么随着程序越来越复杂我们要写的sql语句也越来越多,那么代码中就会出现很多原生的sql语句通过调用数据API(如pymysql,mysql-python)来操作数据库.
并且好多原生的语句是不能重用的,你会发现你在程序的好多地方调用了重复的sql语句.当然,聪明的我们会想到,把这些重复的sql语句,写成函数.但是问题在于你在机在开始开发程序的时候,你没有办法完全预料到所有的操作场景,也就是说没办法预料到要用到什么样的sql语句.所以你还会在实际编程中写很多原生的SQL语句(写死了),并且不能重用.
另外开发人员写出来的sql语句肯定有一些低效率的语句,必定不是专业的DBA.并且如果涉及到多个表查询和做分类的聚合,sql语句如果不优化效率就非常低,所以一般的情况下,DBA会把复杂的sql语句封装成存储过程,让程序员调用.
但是还是有问题,假如现在把存储过程写到代码里,你们公司说我们要把mysql换成oracle,程序员傻逼了,程序里面好多原生的sql和存储过程调用,mysql和oracle语法有很多不一样的地方,那么怎么搞,只能全部重写掉sql语句.
你可能说,我写代码的时候会要求自己写成封装,但是一个项目好多人参与,其他人你能要求,但是万一他在快离职的时候就写一些原生的sql语句,你能怎样,不可控.而且对于个人来说,你封装的sql语句也仅限于当前使用的数据库类型,如果像上面提到的mysql切换成oracle,你就要该封装程序了.
总之,这种方式不可控,而且复杂,完了,这个问题按照自己写代码解决的思路不能解决了.当然我们花上1~3个月来写一个sql的封装的程序,那么我告诉你,可以,你写的就是类似ORM框架的东西,而且90%的可能性,你写的没有SqlAlchemy好.
所以我们最明智的做法是了解SqlAlchemy框架,并且熟练使用他.

下面我们来了解SqlAlchemy是怎么真正的帮我们实现的.
SQLAlcchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简而言之:将对象转化成SQL,然后使用数据API执行SQL并获取执行结果

              

    之前我们学过数据库的API如mysqldb-python直接写sql原生语句,sqlalchemy是建立在数据库API之上,对原生sql语句进行了封装,也就是说你用了sqlalchemy之后,你就不用写sql语句了,你不用写了,这就隔离了.你看看单独这一点就把前面我们遇到的在代码中写原生sql语句的问题解决了,因为你不用写sql语句了嘛.
那你会说代码中原生sql代码的问题解决了,那如何解决从mysql切换到oracle呢?接下来的dialect会根据你配置的不同的数据库类型来调用sqlalchemy内部定义的对于不同数据库类型的api接口.至于你担心的我之前调用的是mysql,现在是oracle的问题,sqlalchemy内部在实例化engine时,根据你连接的数据库类型来选择封装成什么类型的sql原生语句.
紧接着你会问支持多少种数据类型.往下看
(我个人理解的思路是我们知道mysql也好oracle也好,使用的语法会有些不同,那sqlalchemy就像一个中间件,源头是mysql或者oracle又或者是其他的数据库,经过sqlalchemy那么一封装,变成了对于python来说统一的调用模式.就那么简单)
  对上图的具体解释:
SQLALchemy ORM: 就是它的ORM,是对象封装的过程,它把sql语句封装成对象了,他这个对象是怎么封装的?不是说一连上sqlalchemy,就把数据库里所有的东西都封装了,而是你自己要声明,我要把这个表封装成什么对象,把那个表封装成什么对象.整个过程 1.先把原生的表映射成类.2.然后才能通过对象的形势调用它.
schema/Types: 你怎么进行对象关系映射?定义了一种映射格式,就是说你要把这个表映射成这个类,就通过这个框架格式
SQL Expression Language:增删改查的SQL封装语句.
Engine: 封装后你需要通过一个引擎操作它
connection pooling:连接池
Dialect:根据你的配置文件,连接不同的数据库
DBAPI:具体的API,mysql api或者oracle api

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


案例一:
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
 1     #!/usr/bin/env python
 2     # -*- coding:utf-8 -*-
 3       
 4     from sqlalchemy import create_engine
 5       
 6       
 7     engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
 8       
 9     engine.execute(
10         "INSERT INTO ts_test (a, b) VALUES ('2', 'v1')"
11     )
12       
13     engine.execute(
14          "INSERT INTO ts_test (a, b) VALUES (%s, %s)",
15         ((555, "v1"),(666, "v1"),)
16     )
17     engine.execute(
18         "INSERT INTO ts_test (a, b) VALUES (%(id)s, %(name)s)",
19         id=999, name="v1"
20     )
21       
22     result = engine.execute('select * from ts_test')
23     result.fetchall()

  总结:使用sqlalchemy可以像使用数据库api一样直接调用原生代码。这种方式没有什么意义,我们只需要知道可以这么做即可。



步骤二sqlalchemy使用的中间状态:

使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 进行数据库操作。
Engine使用Schema Type创建一个特定的结构对象,之后通过SQL Expression Language将该对象转换成SQL语句,然后通过 ConnectionPooling 连接数据库,再然后通过 Dialect 执行SQL,并获取结果。
 1 #!/usr/bin/env python3.5
 2 #__author__:'ted.zhou'
 3 '''
 4 sqlalchemy 的用法案例
 5 使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 进行数据库操作
 6 '''
 7 #!/usr/bin/env python
 8 # -*- coding:utf-8 -*-
 9 
10 from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
11  #  老师:MetaData需要理解下,就是在创建类时,sqlalchemy在MetaData里封装了好多的关于这个类的一些属性.包括怎么把Column,Integer,String,Table等等,转成相应的语句.
12  # 网络上资料解释: MetaData是元数据,元数据就是描述数据的数据,比如小明 身高:175cm,身高就是元数据,175cm就是实际数据.而在sqlalchemy里的MetaData实例化一个对象,就是一个元数据对象,里面具体的元数据有表名Table,列名Column,以及怎么把Column,Integer,String,Table等等,转成相应的语句方法名称.
13  # 总之,元数据必须有
14 metadata = MetaData()   # 实例化一个属性对象
15 user = Table('user', metadata,          # 实例化一个Table,并和metadata进行绑定.这些表的信息都属于metadata里的.
16     Column('id', Integer, primary_key=True),
17     Column('name', String(20)),
18 )
19 
20 color = Table('color', metadata,
21     Column('id', Integer, primary_key=True),
22     Column('name', String(20)),
23 )
24 # engine = create_engine("mysql+mysqldb://root:123.com@localhost:3306/test", max_overflow=5)
25 
26 engine = create_engine("mysql+pymysql://root:123456@localhost:3307/test",max_overflow=5,echo=True)
27 
28 # 创建一个连接引擎
29 # create_engine("数据库类型+数据库驱动://数据库用户名:数据库密码@IP地址:端口/数据库",其他参数)
30 # 上文当中echo=True是开启调试,这样当我们执行文件的时候会提示相应的文字。
31 
32 metadata.create_all(engine)
33 #
34 # 这一步的具体执行内容就是下面说的.
35 # Engine使用Schema Type创建一个特定的结构对象,之后通过SQL Expression Language将该对象转换成SQL语句,然后通过 ConnectionPooling 连接数据库,再然后通过 Dialect 执行SQL,并获取结果。
步骤二sqlalchemy使用的中间状态的增删改查语法:
(建议只参考下,不需要记住,因为我们最终不会使用这种状态)
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
 5 
 6 metadata = MetaData()           # 实例化元数据对象
 7  # 创建表结构,我个人的理解是这里的创建相当于在python的sqlalchemy中虚拟一个表结构,如果数据库中有相应的表,那么不会在数据库中创建,如果实际数据库中没有,那么需要通过metadata.create_all(engine)来创建,可参见案例一
 8  # 所以可知,创建表结构有两个功能:1.不紧可以声明元数据 2.而且可以用来在数据库中创建表.
 9 user = Table('user', metadata,
10 
11     Column('id', Integer, primary_key=True),
12     Column('name', String(20)),
13 )
14 
15 color = Table('color', metadata,
16     Column('id', Integer, primary_key=True),
17     Column('name', String(20)),
18 )
19 
20 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接,要真的连接需要调用connect方法
21 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True)
22 
23 conn = engine.connect()  # 引擎执行connect()方法,连接到数据库,获取到光标(游标)
24 
25 # 创建SQL语句,INSERT INTO "user" (id, name) VALUES (:id, :name)
26 conn.execute(user.insert(),{'id':10,'name':'seven'})      # 这里我们之所以可以使用user.insert()进行数据插入,而不用写原生sql,是因为我们前面做了将user变量加入到了元数据实例metadata中,这样metadata才知道怎样将user.insert这段代码转化成对应的存储引擎的sql语句
27                                                         # 所以说metadata必须加
28 conn.close()
29 
30 # 插入
31 # sql = user.insert().values(id=123, name='wu')  # user表
32 # conn.execute(sql)
33 # conn.close()
34 
35 # 删除
36 # sql = user.delete().where(user.c.id > 1)  # user.c.id
37 
38 # 改
39 # sql = user.update().values(fullname=user.c.name)
40 # sql = user.update().where(user.c.name == 'jack').values(name='ed')
41 
42 # 查询,查询和上面的插入,删除,改不一样,他使用单独的select方法,不需要像user.select,原因是查询的时候有时候会进行外连接,不是只针对一个表进行的.所以从实际出发sqlalchemy模块中有一个单独的select方法
43 # sql = select([user, ])            # 查询所有
44 # sql = select([user.c.id, ])       # 查字段
45 # sql = select([user.c.name, color.c.name]).where(user.c.id==color.c.id)    # 查字段,设置where条件
46 # sql = select([user.c.name]).order_by(user.c.name)       # 查名称,排序
47 # sql = select([user]).group_by(user.c.name)              # 分组统计
48 
49 # 执行查询
50 # result = conn.execute(sql)
51 # print result.fetchall()      #
52 # conn.close()
View Code

  接下来,我们就看看实际编程中我们使用sqlalchemy的终极状态案例:

SqlAlchemy完整的例子(简单)_用法的终极状态.py
 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine  # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base # 导入sqlalchemy的一个扩展模块declarative_base,此模块调用返回一个封装好的 metadata元数据和表名以及表字段 的类.注意返回的是个类,不是实例.
 6 from sqlalchemy import Column,Integer,String # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例
 8                                             # 这句话的意思,拿到数据库API(传入的bind=engine参数作中记录了数据库的API和连接信息),创建一个连接的类.这个类里有连接信息,和数据库API信息,接下来要创建一个连接session就要实例化这个类
 9 
10 '''
11 使用sqlalchemy ORM完成对数据库的操作整个代码实现过程,可以理解成如下:
12 1.如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类,这里请注意,一个SqlORM类实际上操作数据库的单位不是库,而是表,因为连接的时候直接选择到库.
13 2.把表结构映射出去后,sqlalchemy内部需要一个内部映射的规则比如表名怎么映射,字段怎么映射,String类型怎么映射,Integer怎么映射,删除怎么搞等等,也就是前面说的元数据metadata ,元数据和映射后的表结构绑定后,就可以操作这张表(增删改查).
14 这里是使用declarative_base模块将前两步需要做的事情封装到一个类中.然后下面要创建的SqlORM子类就要继承这个基类了.    封装了映射方法和元数据方法的模块就是declarative_base
15 3.上面两部完成后是只是说可以对映射的数据表进行操作了,但是操作是在本地的,你需要通过连接把SqlORM中的操作最终翻译成对应数据库类型的原生SQL语句,在调用数据库API进行操作. 那么这个引擎的实现模块就是create_engine
16 那么这个连接信息我们称之为引擎.
17 4.引擎相当于一个连接工具,那么谁来使用这个引擎来对数据进行操作呢? 那就是session,session通过引擎调用一系列映射后的表的操作方法,然后翻译成原生SQL后调用数据库API.最终完成对数据库的操作.这个实现session的模块即是sessionmaker
18 '''
19 
20 Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)
21 
22 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
23 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
24 
25 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
26     __tablename__ = 'hosts'  # 声明表名
27     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
28     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
29     ip_addr = Column(String(128),unique=True,nullable=False)
30     port = Column(Integer,default=22)
31 
32 # 创建表结构方法
33 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
34 
35 if __name__ == '__main__':
36     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
37     session = SessionCls()  # 实例化一个连接huihua
38     '''
39     # 增加纪录
40     h1 = Host(hostname='localhost',ip_addr='127.0.0.1')      # 使用前面定义好SqlORM类Host,实例化一个对象,此对象包含了转换原生sql语句的规范.
41     h2 = Host(hostname='ubuntu',ip_addr='10.0.0.1',port=2000)
42     h3 = Host(hostname='ubuntu2',ip_addr='192.168.2.244',port=20000)
43     session.add(h3)     # 可以一条一条添加
44     session.add_all([h1,h2]) # 也可以一次添加多条
45     #h2.hostname = 'ubuntu_test' # 只要没提交,此时修改也没问题
46     #session.rollback()  # 撤销
47     session.commit()    # 最后要提交.
48     '''
49 
50     '''
51     # 更改纪录 (更改纪录首先要查询到这条纪录,然后在进行更改)
52     res = session.query(Host).filter(Host.id == 3 ).first() # 调用session.query(查哪个sqlORM类).fitler(查询条件).first(取几行),res为返回的是一条纪录
53     # res = session.query(Host).filter(Host.hostname == 'ubuntu' ).first() # 调用session.query(查哪个sqlORM类).fitler(查询条件).all()取所有
54     # res = session.query(Host.hostname,Host.id).filter(Host.hostname == 'ubuntu' ).first() # 查询指定字段
55     print("++>",res,res.hostname)
56     res.hostname = 'test server' # 直接更改,
57     session.commit()             # 然后提交
58     '''
59 
60 
61     # 删除操作(删除纪录,先获取对象,然后在删除)
62     res = session.query(Host).filter(Host.hostname.like('%ubuntu%')).all() # 调用session.query获取纪录对象结果集
63     print("------------>",res[0].hostname,res[1].hostname)
64 
65     session.delete(res[0])
66     # print("------------>22",res[0].hostname,res[1].hostname)
67     # session.commit()
sqlalchemy简单例子
SqlAlchemy完整的例子(一对多外键联接1)_用法终极状态
  1.首先1对多外联,必须创建外键
#!/usr/bin/env python3.5
#__author__:ted.zhou
import pymysql
from sqlalchemy import create_engine  
from sqlalchemy.ext.declarative import declarative_base # 注意返回的是个类,不是实例.
from sqlalchemy import Column,Integer,String,ForeignKey 
from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例                    
from sqlalchemy.orm import relationship

Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)

# 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程

class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
    __tablename__ = 'hosts'  # 声明表名
    id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
    hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
    ip_addr = Column(String(128),unique=True,nullable=False)
    port = Column(Integer,default=22)
    groupid = Column(Integer,ForeignKey('group.id'))    # 添加一个字段,并设置此字段类型为外键.
                                                        # 使用ForeignKey设置外键时注意亮点:
                                                        # 1. ForeignKey需要从SqlAlchemy中导入 from sqlalchemy import ForeignKey
                                                        # 2. ForeignKey(参数),参数是实实在在的表名,而不是封装了的SqlAlchemy 的ORM类名
    '''
    执行时原生语句的内容如下:
    CREATE TABLE hosts (
    id INTEGER NOT NULL AUTO_INCREMENT,
    hostname VARCHAR(64) NOT NULL,
    ip_addr VARCHAR(128) NOT NULL,
    port INTEGER,
    groupid INTEGER,
    PRIMARY KEY (id),
    UNIQUE (hostname),
    UNIQUE (ip_addr),
    FOREIGN KEY(groupid) REFERENCES `group` (id) # 这里就是设置外键的语句,可以参考记住
    )
    '''
class Group(Base):
    __tablename__ = 'group' # 定义表名
    id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
    groupname = Column(String(128),unique=True,nullable=False)

Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.

if __name__ == '__main__':
    SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
    session = SessionCls()  # 实例化一个连接huihua
    '''
    # 先创建一个组
    g1 = Group(groupname='g1')
    g2 = Group(groupname='g2')
    g3 = Group(groupname='g3')
    session.add_all([g1,g2,g3])
    # 创建
    h1 = Host(hostname='linux1',ip_addr='10.0.0.1',port='22',groupid=g1.id)
    h2 = Host(hostname='ubuntu1',ip_addr='10.0.0.2',port='33')
    session.add_all([h1,h2])
    session.commit()

    我们在这一连串的代码中实现了,添加组,然后创建主机,设置h1的groupid = g1.id
    但是结果是 h1的groupid最终加到数据库中的值为NULL,为什么?
    原因是 你虽然在映射后的sqlalchemy的orm类实例化了g1,但是在数据库表中没有,并且你在实例化g1时,没有直接指定g1.id值,所以接下来h1实例化的时候取不到g1.id的值
    那么问题来了,如果我在实例化g1的时候指定了id的值能不能成功呢.当然实际应用中自增长字段是 不会自己指定值的,但这里测试.我猜测是可以的.下面我们做这个测试.

    '''

    '''
    # 测试
    g4 = Group(id=4,groupname='g4')
    h3 = Host(hostname='ubuntu2',ip_addr='10.0.0.3',port=2000)
    h4 = Host(hostname='linux2',ip_addr='10.0.0.4',port=200,groupid=g4.id)
    session.add_all([g4,h3,h4])
    session.commit()
    # 执行结果:
    # mysql> select * from hosts;
    # +----+----------+----------+------+---------+
    # | id | hostname | ip_addr  | port | groupid |
    # +----+----------+----------+------+---------+
    # |  1 | linux1   | 10.0.0.1 |   22 |    NULL |
    # |  2 | ubuntu1  | 10.0.0.2 |   33 |    NULL |
    # |  3 | ubuntu2  | 10.0.0.3 | 2000 |    NULL |
    # |  4 | linux2   | 10.0.0.4 |  200 |       4 |
    # +----+----------+----------+------+---------+
    # 4 rows in set (0.00 sec)
    # 所以上面上面的判断是正确的
    '''

   上面的例子就是在Host中添加了一个groupid外键到Group.id。

    '''
    接下来,我想知道id=4的host所属组的组名,怎么获取?
      h = session.query(Host).filter(Host.id == 4).first()
      print('+++++++++++++++>',h.groupname) # 打印h.groupname
      # 结果: AttributeError: 'Host' object has no attribute 'groupname'
    说明直接调用是不行的,那么怎样才能获得groupname呢?
    答案:要用到关联查询了,要导入relationship不是relationships,也就是说你希望在Host类里,通过什么字段就可以掉用到groupname,
        于是在class Host类中加入: group = relationship('Group')这里的参数是类,而不是表名.说明通过反射的方法,把Group实例嵌到Host实例中的group字段中.这个时候就可以使用group.groupname获得名称了.

  代码改成如下:

  SqlAlchemy完整的例子(一对多外键联接)2_用法终极状态:

 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine  # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base # 导入sqlalchemy的一个扩展模块declarative_base,此模块调用返回一个封装好的 metadata元数据和表名以及表字段 的类.注意返回的是个类,不是实例.
 6 from sqlalchemy import Column,Integer,String,ForeignKey # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例
 8                                             # 这句话的意思,拿到数据库API(传入的bind=engine参数作中记录了数据库的API和连接信息),创建一个连接的类.这个类里有连接信息,和数据库API信息,接下来要创建一个连接session就要实例化这个类
 9 from sqlalchemy.orm import relationship     # 导入ralationship模块
10 
11 Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)
12 
13 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
14 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
15 
16 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
17     __tablename__ = 'hosts'  # 声明表名
18     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
19     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
20     ip_addr = Column(String(128),unique=True,nullable=False)
21     port = Column(Integer,default=22)
22     groupid = Column(Integer,ForeignKey('group.id'))    # 添加一个字段,并设置此字段类型为外键.
23     group = relationship('Group') # 建立关联关系
24 
25 class Group(Base):
26     __tablename__ = 'group' # 定义表名
27     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
28     groupname = Column(String(128),unique=True,nullable=False)
29 
30 # 创建表结构方法
31 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
32 
33 if __name__ == '__main__':
34     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
35     session = SessionCls()  # 实例化一个连接huihua
36     '''
37     再次测试!!!
38     '''
39     h = session.query(Host).filter(Host.id == 4).first()
40     print('+++++++++++++++>',h.group.groupname) # 打印h.group.groupname
41     # 执行结果: +++++++++++++++> g4
        总结:要用到关联查询,两部:1.导入sqlalchemy.orm模块中的relationship 2.使用relationship建立关联关系.

刚刚我们实现了一对多,一个主机对应一个组,并且通过ralationship()建立关系后,使用host.group.groupname获得组名。

那么问题来了,我们如何实现通过组,获得组中包含多少个主机呢?
答案:前面我们只是在Host 类中声明了relationship(),所以通过host.group.groupname能过获得groupname的信息,
所以如果我们想通过group.hosts获得主机的话,就需要在relationship()中声明和Host的关系。
于是代码改成如下:
SqlAlchemy例子(一对多外键联接3)_通过组获取主机_双方声明关系
 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine  # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base # 导入sqlalchemy的一个扩展模块declarative_base,此模块调用返回一个封装好的 metadata元数据和表名以及表字段 的类.注意返回的是个类,不是实例.
 6 from sqlalchemy import Column,Integer,String,ForeignKey # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例
 8                                             # 这句话的意思,拿到数据库API(传入的bind=engine参数作中记录了数据库的API和连接信息),创建一个连接的类.这个类里有连接信息,和数据库API信息,接下来要创建一个连接session就要实例化这个类
 9 from sqlalchemy.orm import relationship     # 导入ralationship模块
10 
11 Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)
12 
13 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
14 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
15 
16 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
17     __tablename__ = 'hosts'  # 声明表名
18     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
19     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
20     ip_addr = Column(String(128),unique=True,nullable=False)
21     port = Column(Integer,default=22)
22     groupid = Column(Integer,ForeignKey('group.id'))    # 添加一个字段,并设置此字段类型为外键.
23     group = relationship('Group') # 建立关联关系
24 
25 class Group(Base):
26     __tablename__ = 'group' # 定义表名
27     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
28     groupname = Column(String(128),unique=True,nullable=False)
29     hosts = relationship('Host') # 在组类中声明和Host类的关联
30 
31 # 创建表结构方法
32 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
33 
34 if __name__ == '__main__':
35     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
36     session = SessionCls()  # 实例化一个连接huihua
37     '''
38     # 我们先测试
39     g4 = session.query(Group).filter(Group.id == 4).all()
40     print("+++++++++>",g4.host)
41     #结果不行,因为Group中没有host字段
42     # 是不是应该在Group类中同样要通过relationship('Host') 绑定关系,在Group类中添加语句如下:    hosts = relationship('Host')
43     '''
44 
45     '''
46     # 接着测试:
47     g4 = session.query(Group).filter(Group.id == 4).first()  # 这里要用first(),不能用all()否则得到的是一个列表
48     print("+++++++++>",g4.hosts)
49     # 结果:+++++++++> [<__main__.Host object at 0x103843048>] ,如果有多个,那么得到的是两个obj对象64     '''
反向就查出来了,是不是很方便,能够这么使用的原因:1.Host中的groupid和Group中的id外键关联,如果没有关联关系肯定不行
2. Group类中也要调用relationship('Host')进行关联
那么问题来了,一个父亲,要和多个儿子做ralationship()方法的关联,这代码看上去是不是很麻烦.
sqlalchemy语法也考虑到了,只需要在儿子类中把relationship()语法写成: hosts = relationship('Host',backref='hosts')
于是代码改成如下:
SqlAlchemy例子(一对多外键联接4)_通过组获取主机_一方声明双方起作用
 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine  # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base 
 6 from sqlalchemy import Column,Integer,String,ForeignKey # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类              9 from sqlalchemy.orm import relationship     # 导入ralationship模块
10 
11 Base = declarative_base() 
12 
13 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
14 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
15 
16 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
17     __tablename__ = 'hosts'  # 声明表名
18     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
19     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
20     ip_addr = Column(String(128),unique=True,nullable=False)
21     port = Column(Integer,default=22)
22     groupid = Column(Integer,ForeignKey('group.id'))    # 添加一个字段,并设置此字段类型为外键.
23     group = relationship('Group',backref='hosts') # 建立关联关系,这一句很关键,一句代码就实现了双向关系的声明
24 
25 
26 class Group(Base):
27     __tablename__ = 'group' # 定义表名
28     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
29     groupname = Column(String(128),unique=True,nullable=False)
30     # hosts = relationship('Host') # 这里就不需要用了
31 
32 # 创建表结构方法
33 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
34 
35 if __name__ == '__main__':
36     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
37     session = SessionCls()  # 实例化一个连接huihua
38 
39 
40     # 接着测试:
41     g4 = session.query(Group).filter(Group.id == 4).first()  # 这里要用first(),不能用all()否则得到的是一个列表
42     print("+++++++++>",g4.hosts)
43     # 结果:+++++++++> [<__main__.Host object at 0x103843048>] ,如果有多个,那么得到的是两个obj对象
  总结:用group = relationship('Group',backref='hosts') # 可以建立双向关系
同时还有一个很类似backref 的用法,就是relationship('Group',back_populates='hosts')
那么我们会问为什会有一个累死backref的功能呢,它用在什么时候.一点区别没有吗.
区别是: 用法上不一样relationship('Group',back_populates='hosts')必须是成对出现的,
也就是说在儿子处group = relationship('Group',back_populates='hosts'),在父亲处就必须有 hosts = relationship('Host',back_populates='group')
那么我们猜想应用场景也就是做统一管理接口的时候,就是在父亲处统一管理,让儿子可不可以外链它.增加了安全性。
前面一对多外联的基本用法演示完成,我们知道实际开发中有很多使用join的方法进行外联查询
SqlAlchemy例子(一对多外键联接5)_innerjoin_leftjoin_rightjoin
 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base # 导入sqlalchemy的一个扩展模块declarative_base,此模块调用返回一个封装好的 metadata元数据和表名以及表字段 的类.注意返回的是个类,不是实例.
 6 from sqlalchemy import Column,Integer,String,ForeignKey,func # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例
 8                                             # 这句话的意思,拿到数据库API(传入的bind=engine参数作中记录了数据库的API和连接信息),创建一个连接的类.这个类里有连接信息,和数据库API信息,接下来要创建一个连接session就要实例化这个类
 9 from sqlalchemy.orm import relationship     # 导入ralationship模块
10 
11 Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)
12 
13 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
14 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
15 
16 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
17     __tablename__ = 'hosts'  # 声明表名
18     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
19     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
20     ip_addr = Column(String(128),unique=True,nullable=False)
21     port = Column(Integer,default=22)
22     groupid = Column(Integer,ForeignKey('group.id'))    # 添加一个字段,并设置此字段类型为外键.
23     group = relationship('Group',backref='hosts') # 建立关联关系
24 
25 
26 class Group(Base):
27     __tablename__ = 'group' # 定义表名
28     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
29     groupname = Column(String(128),unique=True,nullable=False)
30     # hosts = relationship('Host') # 这里就不需要用了
31 
32 class test_table(Base):
33     __tablename__ = 'test_table'
34     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
35     name2 = Column(String(64),unique=True,nullable=False)
36 
37 # 创建表结构方法
38 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
39 
40 if __name__ == '__main__':
41     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
42     session = SessionCls()  # 实例化一个连接huihua
43     '''
44     # inner join
45     obj = session.query(Host).join(Host.group).filter().all()
46     print('+++++++++>',obj)
47     # 执行时,调用的原生SQL语句是:SELECT hosts.id AS hosts_id, hosts.hostname AS hosts_hostname, hosts.ip_addr AS hosts_ip_addr, hosts.port AS hosts_port, hosts.groupid AS hosts_groupid FROM hosts INNER JOIN `group` ON `group`.id = hosts.groupid
48     # 分析原生sql,得出结论:join(Host.group)这段代码,首先说明1. Host 中有group字段,并且group字段是通过relationship()做关联的,那通过什么关联的呢 继而得出条件2.Host 和Group有外键
49 
50     # 上面得到的也只有一张表的数据,那如果想得到两张表的数据呢
51     obj2 = session.query(Host,Group).join(Host.group).filter().all()
52     print("obj2:",obj2)
53     # 解析成原生sql为:
54     # SELECT hosts.id AS hosts_id, hosts.hostname AS hosts_hostname, hosts.ip_addr AS hosts_ip_addr, hosts.port AS hosts_port, hosts.groupid AS hosts_groupid, `group`.id AS group_id, `group`.groupname AS group_groupname FROM hosts INNER JOIN `group` ON `group`.id = hosts.groupid
55     '''
56     '''
57     # 上面两个都是有建立过外键的情况下进行查询,假如没有创建过字段为外键的情况下能查询呢
58     obj3 = session.query(Host,Group).join(Host.groupid==Group.id).filter().all()
59     print("obj3:",obj3)
60     # 经测试,行不通,难道说join查询在sqlalchemy中只支持带外键的查询吗.未知???这将对我了解数据库有很大帮助
61     '''
62 
63     # 使用group_by 对查询结果进行分类聚合
64     obj4 = session.query(func.count(Host,Group)).join(Group).filter().group_by(Group.groupname).first()
65     print('obj4:',obj4)
66     '''
67     # 下面我们测试下当没有外键关系的表,外链查询的结果
68     obj5 = session.query(Host,test_table).join(test_table).filter().all()
69     print('obj5:',obj5)
70     # 结果报错:sqlalchemy.exc.NoForeignKeysError: Can't find any foreign key relationships between 'hosts' and 'test_table'.
71     # 说明要执行join查询需要有外键关系.
72     # 但是原生的sql是可以查询没有外键关系的两种表的
73     # SELECT hosts.id AS hosts_id, hosts.hostname AS hosts_hostname, hosts.ip_addr AS hosts_ip_addr, hosts.port AS hosts_port, hosts.groupid AS hosts_groupid  FROM hosts INNER JOIN `test_table` ON `test_table`.id = hosts.groupid;
74     # 结果:Empty set (0.00 sec)
75     # 那么接下来又一个问题要解决,就是在sqlalchemy中如何实现没有外键关系进行 join查询???
76     '''
1对多外联查询演示

前面SqlAlchemy完整的例子(一对多外键联接1-5) 这些都是一对多的外链,从基础到高级用法.
但实际开发中,经常有多对多的场景,比如一个主机属于多个组.前面1-5个例子都无法实现,因为每一个主机一条纪录,主机名是唯一的,你没有办法为同一个主机名创建2条纪录,而只是主机组id不一样.
即使可以,也不符合我们作为技术的宗旨,同一样的纪录最好只需要录入一遍.所以如果想实现一个主机属于多个组,就应该单独创建一张主机——组的关系表.表中只纪录主机id和组id.并且不论你用不用orm所有数据库表结构的many to many的关系,也都是通过关系表实现
在原生sql中,拿mysql来说,看起来是没有什么难度的.
假设我们现在创建了一个表:
表名:HostToGroup
id host_id group_id
1 1 1
2 1 2
3 2 1
那么我们想通过关系表查找 主机名,组名,是不是每次查都需要各种来回的join查询.
而orm的存在简化这种操作.实现了不用调用中间表,通过
Host.groups()就可以查出这个主机所属的组的列表
group.hosts() 就可以查出这个组中包含的所有主机的列表.
具体内部代码如何实现的,我们不需要知道,只要知道如何使用sqlalchemy代码实现就可以了.
代码范例:
SqlAlchemy例子(对对多外键联接1)_对原生sql优化的内容
  1 #!/usr/bin/env python3.5
  2 #__author__:ted.zhou
  3 import pymysql
  4 from sqlalchemy import create_engine
  5 from sqlalchemy.ext.declarative import declarative_base
  6 from sqlalchemy import Column,Integer,String,ForeignKey,func # 导入和创建表结构相关的模块
  7 from sqlalchemy.orm import sessionmaker       9 from sqlalchemy.orm import relationship     # 导入ralationship模块
 11 from sqlalchemy import Table # 要先导入Table,接下来我们使用Table来创建关系表,而不是使用类的形势创建关系表
 12 
 13 Base = declarative_base() #生成一个SqlORM基类
 14 
 15 Host2Group = Table('host_2_group',Base.metadata,        # 这里为什么用Table方式创建表,而不使用class方式,首先我们要知道Table方式创建的表返回的是一个实例,我们可以通过实例.insert()插入数据.而class插入数据的时候却要先实例化.所以我们这里是要用到它返回实例可以操作的特性,用到Table,而不用class
 16                    Column('host_id',ForeignKey('hosts.id'),primary_key=True),   # 这里我们看这个表中有两个主键,为什么呢
 17                    Column('group_id',ForeignKey('group.id'),primary_key=True),  # 因为确保 (主机id-组id) 唯一,且他们两个都不能为空
 18                    )
 19 '''
 20 这里表创建好了,并且使用的是Table,
 21 我们说了sqlalchemy优化了这种many to many的关系实现方式.目前知道能简化的方式:
 22 1.不用通过join调用,直接通过Host.groups()获得某个主机所属的组,直接通过group.hosts()获得某个组中的主机
 23 那我现在告诉你第二个优化的点
 24 2.在新建一个主机 并且关联到某一个组时,自动的往关系表中添加一条host_id和group_id的纪录.(是不是很高级~自动添加,不用像原生SQL一样,要多写一条sql语句)
 25 这也是前面使用Table创建这个关系表的原因
 26 紧接着你会问怎么实现呢? 別着急,很简单,还记得我们使用relationship()方法,来指定hosts表和group表的关联关系吗?
 27 我们只需要在原有relationship()方法中,加secondary=Host2Group,并且把之前创建的外键字段删除
 28 最终应该是
 29 groups = relationship('Group',secondary=Host2Group,backref='hosts')
 30 '''
 31 
 32 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
 33 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
 34 
 35 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
 36     __tablename__ = 'hosts'  # 声明表名
 37     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
 38     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
 39     ip_addr = Column(String(128),unique=True,nullable=False)
 40     port = Column(Integer,default=22)
 41     # groupid = Column(Integer,ForeignKey('group.id'))    # 此时要删除这个外键了
 42     # group = relationship('Group',backref='hosts') # 这个关系也要改了
 43     groups = relationship('Group',
 44                           secondary=Host2Group, # 指定中间表的实例
 45                           backref='hosts') # 最终改成这个样子了
 46 
 47     '''
 48     # 因为关系表两个外键一个host_id,外键的是hosts.id
 49     # 另外一个外溅group_id,外键的是group.id
 50     # 就是根据这两个外键,并且通过relationship()方法才做了优化 many to many关系的操作语法.
 51     '''
 52 
 53 class Group(Base):
 54     __tablename__ = 'group' # 定义表名
 55     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
 56     groupname = Column(String(128),unique=True,nullable=False)
 57     # hosts = relationship('Host') # 这里就不需要用了
 58 
 59 
 60 # 创建表结构方法
 61 Base.metadata.create_all(engine)  
 62 
 63 if __name__ == '__main__':
 64     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
 65     session = SessionCls()  # 实例化一个连接huihua
 66 
 67     '''
 68     一 首先我们先创建主机和组
 69     # 创建3个组
 70     # g1 = Group(groupname='g1',id=1)
 71     # g2 = Group(groupname='g2',id=2)
 72     # g3 = Group(groupname='g3')
 73 
 74     # 创建一个主机
 75     # h1 = Host(hostname='linux1',ip_addr='10.0.0.1',port=22)
 76 
 77     #
 78     # session.add_all([g1,g2,g3,h1])
 79     # session.commit()
 80     '''
 81     '''
 82     二. 然后我们将 主机和组关联起来
 83     groups = session.query(Group).filter().all() # 查出所有组
 84     h = session.query(Host).filter(Host.hostname == 'linux1').first() # 查出一个主机
 85     h.groups = groups       # 将这个主机的groups 直接赋值 上面所查到的groups
 86     session.commit()        # 提交后,就会在关系表中host_2_group 自动创建3条纪录,前提就是1.relationship()的设置,和2.关系表外键的设置
 87     '''
 88 
 89     '''
 90     # 三 .接下来我们来试试查询
 91     # 查看主机所属的所有组
 92     h = session.query(Host).filter(Host.hostname=='linux1').first()
 93     print('++++++++++++>>>>>',h.groups)
 94     # 执行结果:
 95     # ++++++++++++>>>>> [<__main__.Group object at 0x103717748>, <__main__.Group object at 0x1037177b8>, <__main__.Group object at 0x103717828>]
 96     # 我们看到有3个组的对象
 97     # 查看组里所有的主机
 98     g = session.query(Group).filter(Group.groupname == 'g2').first()
 99     print('----------->>>>>>',g.hosts)
100     # 执行结果:
101     # ----------->>>>>> [<__main__.Host object at 0x10383d320>]
102     # 我们看到有一个主机对象.
      总结:sqlalchemy是多么的神奇.好的很
     前面查询的host.groups 和 group.hosts的结果都是对象类型,那么我们最终要打印的肯定不是对象,而是字符串.
问题:怎样将结果打印成字符串呢. sqlalchemy语法本身是没有方法的,我们看我们是通过class创建的orm类,那么我们可以定义__repr__()方法,来随意指定返回的内容
因此 我们在创建 Host和Group类中定义 def __repr__(self):
于是代码改成如下:
SqlAlchemy例子(对对多外键联接2)_将返回的结果转换成字符串
 1 #!/usr/bin/env python3.5
 2 #__author__:ted.zhou
 3 import pymysql
 4 from sqlalchemy import create_engine # 导入引擎类
 5 from sqlalchemy.ext.declarative import declarative_base # 导入sqlalchemy的一个扩展模块declarative_base,此模块调用返回一个封装好的 metadata元数据和表名以及表字段 的类.注意返回的是个类,不是实例.
 6 from sqlalchemy import Column,Integer,String,ForeignKey,func # 导入和创建表结构相关的模块
 7 from sqlalchemy.orm import sessionmaker     # 导入sessionmaker模块,它自己是一个类,实例化它返回的还是一个类(是一个包含了数据库API信息和连接地址端口以及用户名和密码信息的类)不是一个实例
 8                                             # 这句话的意思,拿到数据库API(传入的bind=engine参数作中记录了数据库的API和连接信息),创建一个连接的类.这个类里有连接信息,和数据库API信息,接下来要创建一个连接session就要实例化这个类
 9 from sqlalchemy.orm import relationship     # 导入ralationship模块
10 
11 from sqlalchemy import Table # 要先导入Table,接下来我们使用Table来创建关系表,而不是使用类的形势创建关系表
12 
13 Base = declarative_base() #生成一个SqlORM基类,接下来所有要创建的SqlORM的子类(理解成如果想用sqlalchemy来操作数据库中的表,就需要把数据库的表结构映射成SqlORM类)
14 
15 Host2Group = Table('host_2_group',Base.metadata,        # 这里为什么用Table方式创建表,而不使用class方式,首先我们要知道Table方式创建的表返回的是一个实例,我们可以通过实例.insert()插入数据.而class插入数据的时候却要先实例化.所以我们这里是要用到它返回实例可以操作的特性,用到Table,而不用class
16                    Column('host_id',ForeignKey('hosts.id'),primary_key=True),   # 这里我们看这个表中有两个主键,为什么呢
17                    Column('group_id',ForeignKey('group.id'),primary_key=True),  # 因为确保 (主机id-组id) 唯一,且他们两个都不能为空
18                    )
19 
20 # 实例化引擎,只是把连接信息作为参数传入,并未真的连接.
21 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3307/test",echo=True) # echo=True可以显示转化后的原生SQL语句执行过程
22 
23 class Host(Base):            # 定义一个SqlORM类,继承Base类这样他就默认绑定了matedata和数据结构
24     __tablename__ = 'hosts'  # 声明表名
25     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)    # 声明字段id,类型为int,为主键,自增长属性为真
26     hostname = Column(String(64),unique=True,nullable=False) # 声明字段hostname,类型为String,是否唯一限制为True,是否可为空False
27     ip_addr = Column(String(128),unique=True,nullable=False)
28     port = Column(Integer,default=22)
29     groups = relationship('Group',secondary=Host2Group,backref='hosts') # 最终改成这个样子了
30 
31     def __repr__(self):             # 定义__repr__方法,默认返回字符串
32         return "<id=%s,hostname=%s,ip_addr=%s>"%(self.id,
33                                                  self.hostname,
34                                                  self.ip_addr)
35 
36 class Group(Base):
37     __tablename__ = 'group' # 定义表名
38     id = Column(Integer,primary_key=True,autoincrement=True,nullable=False)
39     groupname = Column(String(128),unique=True,nullable=False)
40 
41     def __repr__(self):             # 定义__repr__方法,默认返回字符串
42         return "<id=%s,name=%s>"%(self.id,self.groupname)
43 # 创建表结构方法
44 Base.metadata.create_all(engine)   # 通过引擎engine,SqlORM基类 调用metadata中的create_all方法,这样远程数据库中如果存在子类中的表则不做动作,如果不存在则添加.如果存表名但是字段不一样也不做操作.
45 
46 if __name__ == '__main__':
47     SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例,也就意味着一切连接都就绪了,就查你点连接了
48     session = SessionCls()  # 实例化一个连接huihua
49 
50 
51     # 查看主机所属的所有组
52     h = session.query(Host).filter(Host.hostname=='linux1').first()
53     print('++++++++++++>>>>>',h.groups)
54 
55     # 打印结果:
56     # ++++++++++++>>>>> [<id=1,name=g1>, <id=2,name=g2>, <id=3,name=g3>]
57 
58 
59     g = session.query(Group).filter(Group.groupname == 'g2').first()
60     print('----------->>>>>>',g.hosts)
61     # 打印结果
62     # ----------->>>>>> [<id=1,hostname=linux1,ip_addr=10.0.0.1>]
请仔细查看上面两个范例代码,保证能看到你想看到的东西
至此,sqlalchemy的一些简单用法,外键用法,join用法,以及多对多关系的用法这节课就讲完了。基本上能够满足70%了,其他的实际需求要自己查。






 
原文地址:https://www.cnblogs.com/zhming26/p/5626492.html