• 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%了,其他的实际需求要自己查。






     
  • 相关阅读:
    Postgresql HStore 插件试用小结
    postgres-xl 安装与部署 【异常处理】ERROR: could not open file (null)/STDIN_***_0 for write, No such file or directory
    GPDB 5.x PSQL Quick Reference
    postgresql 数据库schema 复制
    hive 打印日志
    gp与 pg 查询进程
    jquery table 发送两次请求 解惑
    python 字符串拼接效率打脸帖
    postgresql 日期类型处理实践
    IBM Rational Rose软件下载以及全破解方法
  • 原文地址:https://www.cnblogs.com/zhming26/p/5626492.html
Copyright © 2020-2023  润新知