zoukankan      html  css  js  c++  java
  • 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%了,其他的实际需求要自己查。






     
  • 相关阅读:
    flume+kafka+storm打通过程
    kafka入门
    hive执行更新和删除操作
    redis存储对象与对象序列化详解
    语音常识--语音信号的数字模型
    声源测向: TDOA-GCC-PATH方法
    【面试】如何比较一个类型【模板使用】【sizeof用法】
    【概括】C++11/14特性
    【面试】编译器为我们实现了几个类成员函数?(c++)
    语音信号处理常识【摘抄|自用】
  • 原文地址:https://www.cnblogs.com/zhming26/p/5626492.html
Copyright © 2011-2022 走看看