• ORM框架与mysql数据库的无缝对接


    ORM
    现在咱们聊聊深入操作数据库了。
    大家都知道,使用数据库的原生语句来写,一旦数据量了,首先就得疯狂重复写代码,而且还基本不怎么重用,都是一次写死,后面需要又得再写,那么面对对象编程的核心里面教你一个面对对象数据库,这个时候咱们看效果
    普通sql数据库建表一开始这样:

    1 create table table_name(id int not null auto_increment,
    2                         name varchar(32),
    3                         password varchar(32),
    4                          primary key(id)
    5                          )

    这是非常简单的sql的表,如果复杂点,加些什么关联的,估计头都要炸,那么,orm是什么鬼?它是一个框架,封装了数据库原语,我们可以面向对象来玩弄数据库了。

    import sqlalchemy
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import create_engine
    from sqlalchemy import Colum,Integer,String
    from sqlalchemy.orm import sessionmaker
    
    engine=create_engine('mysql+pymysql://root:123456@localhost/slkdb',encoding ='utf-8',echo=True)
    '''
    注意写法,orm支持很多借口,我这里是用pymysql,所以是
    mysql+pymysql://<user>:<password>@<host>/<dbname> #user是用户名,password是密码,host是数据库所在ip地址,dbname是要处理的数据表名字,什么,你的数据表不支持中文?报错?什么拉丁的错?我告诉你,只要在dbname后面加一个?charset=utf8 既可解决
    
    中文支持写法:engine=create_engine('mysql+pymysql://root:123456@localhost/slkdb?charset=utf8',encoding ='utf-8',echo=True)
    
    由于我是python3,不支持安装MySQL-Python这个模块,这个接口对应的创建就不写了。
    '''
    Base = declarative_base()#生成orm基类
    class User(Base):
        __tablename__='user'#表名
        id = Colum(Integer,primary_key=True)
        name = Colum(String(32))
        password = Colum(String(32))
    Base.metadata.create_all(engine)#创建表结构
    session_class=sessionmaker(bind=engine)#创建一个数据库的会话,注意这里只是创建了对象
    session=session_class()#此时才是生成实例,相当于cursor
    User1=User(name='hehe',password = '456456')#生成要生成的数据对象
    User2=User(name='zhaff',password = '298789')#生成要生成的数据对象
    session.add(User1)
    session.add(User2)
    session.commit()#此时提交后才最终把数据创建完毕
    

      

    好了 数据也插入进去了运行结果信息:
    --------------------------------------------

    2018-03-06 15:49:45,082 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
    2018-03-06 15:49:45,082 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
    2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'
    2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,086 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
    2018-03-06 15:49:45,086 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
    2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
    2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,088 INFO sqlalchemy.engine.base.Engine DESCRIBE `user`
    2018-03-06 15:49:45,088 INFO sqlalchemy.engine.base.Engine {}
    2018-03-06 15:49:45,091 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
    2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, password) VALUES (%(name)s, %(password)s)
    2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine {'name': 'hehe', 'password': '456456'}
    2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, password) VALUES (%(name)s, %(password)s)
    2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine {'name': 'zhaff', 'password': '298789'}
    2018-03-06 15:49:45,093 INFO sqlalchemy.engine.base.Engine COMMIT

    -------------------------------------------------------------------
    '''注意:运行过程中可能会出现 Warning: (1366, "Incorrect string value: '\xD6\xD0\xB9\xFA\xB1\xEA...' for column 'VARIABLE_VALUE' at row 481")
    result = self._query(query)
    这样的警告或者其他巴拉巴拉的警告,这个时候忽略它既可,因为他仅仅是警告,并不是error,程序还是正常运行,数据库表内数据已经建立。技术有限,目前并未解决这个问题,后期有机会我优化'''
    大家可能看到这会感觉麻蛋怎么更复杂了的感觉,毕竟那么多包模块的各种乱入还有各种对象创建看起来很复杂,

    好,是时候表演真正的技术了:

    增加数据已经说过,就上面的add,下面说查询

    data = session.query(User).filter(User.id>=1).filter(User.id<4).all()#多条件查询,直接多个filter既可,这里的符号都是比较,单等号会出错哟~

    print(data)

    也可以使用in_([*arg]),表示在arg元组中匹配

    data = session.query(User).filter(User.name.in_(['zhaoxin','zhaff'])).all()
    print(data)

    -------------
    输出:[zhaoxin : 29999, zhaoxin : 29999, zhaff : 298789]

    修改:
    data = session.query(User).filter(User.name=='hehe').all()[0]#多条件查询,这里的all[0]也可以用frist()代替
    print(data)
    data.name = 'sb'
    session.commit()#修改需要提交,不然无法修改,一般单个修改较多,多重修改可以用
    print(data)
    输出结果:
    ------------------------
    hehe : 456456
    sb : 456456
    ------------------

    同理 删除就是delete了
    data = session.query(User).filter(User.name=='sb').all()[0]#多条件查询,这里的all[0]也可以用frist()代替
    print(data)
    data.name = 'sb'
    session.commit()
    如此便删除了名字为'sb'的数据。

    删改查都是先query查到后才做的修改,这点不要弄混了。

    分组统计

    统计大家都知道,count,那么这里自然就是

    data = session.query(User).filter(User.name=='sb').count()#没有all,写了all就相当于一个类了,那么此时count就变成了类方法,错误提示该User类中没有count这个attribute。

    分组统计
    from sqlalchemy import func
    data = session.query(User.name,func.count(User.name)).group_by(User.name).all()
    print(data)
    -----------
    输出:[('zhaff', 1), ('zhaoxin', 2)]
    group_by的作用就和命令行的group by一样,详情移步:http://www.cnblogs.com/Jason504327775/p/8512953.html


    连表查询:

    此时如果创建了一个新的类对象。
    class Bala(Base):
    *****此处省略了
    同上一样

    那么两个表,我们要关联他们两个,比如表内 name这栏,名字一样的全给我弄出来
    result = session.query(User,Bala).filter(User.name == Bala.name).all()#这种是强制指定name联系的,不需要有外键联系
    输出结果以列表套元组格式出现,每个元组中对应的就是User.name == Bala.name对应的那两条数据的详细信息。这就不演示了。
    有外键联系的可以使用
    result = session.query(User).join(Bala).all()#来实现
    下面orm最牛逼的地方就是外键关联的方便性,直接上代码:

    import sqlalchemy
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import create_engine
    from sqlalchemy import Column,Integer,String,ForeignKey
    from sqlalchemy.orm import sessionmaker,relationship
    
    engine=create_engine('mysql+pymysql://root:123456@localhost/slkdb',encoding ='utf-8')
    Base = declarative_base()#生成orm基类
    class Student(Base):#重新生成两个数据表,之前的都删了重新开始
        __tablename__='student'#表名
        id = Column(Integer,primary_key=True)
        name = Column(String(32),nullable=False)
        register_date = Column(String(32),nullable=False)
    
        def __repr__(self):
            return 'ID:%s,name: %s'%(self.id,self.name)
    class Stu_record(Base):
        __tablename__='stcored'
        id = Column(Integer,primary_key=True)
        day = Column(Integer)
        statue = Column(String(32),nullable=False)
        stu_id = Column(Integer, ForeignKey('student.id'))#外键关联就在这,注意是table_name名.id而不是类对象名.id
        student = relationship('Student',backref='my_study_recode')#这个实现了反向查询,这行代码相当于实例化一个Student的对象实例,此时下面的self.student.name可以调用,通过my_study_recode可以调用Stu_record这个对象的属性,这个函数是从内存中调用,python中封装出来的,并不是数据库里面的功能,如此方便很多倍
        def __repr__(self):
            return '%s第%s天的登录状态%s'%(self.student.name,self.day,self.statue)
    
    Base.metadata.create_all(engine)#创建表结构
    
    session_class=sessionmaker(bind=engine)
    session=session_class()
    s1 = Student(name='zhaoxin',register_date='2011-02-14')
    s2 = Student(name='jianji',register_date='2012-01-14')
    s3 = Student(name='ruizi',register_date='2011-05-14')
    s4 = Student(name='luoli',register_date='2011-06-11')
    t1 = Stu_record(day=1,statue='YES',stu_id=1)
    t2 = Stu_record(day=2,statue='NO',stu_id=1)
    t3 = Stu_record(day=3,statue='YES',stu_id=1)
    t4 = Stu_record(day=4,statue='NO',stu_id=1)
    t5 = Stu_record(day=5,statue='YES',stu_id=2)
    session.add_all([s1,s2,s3,s4,t1,t2,t3,t4,t5])
    session.commit()
    s_obj=session.query(Student).filter(Student.name=='zhaoxin').first()#此时返回的是student这个类
    print(s_obj.my_study_recode)#通过关联,这个关联是内存里进行的通过类之间的映射,反查到Stu_record中所有的属性,并调用Stu_record的__repr__函数。
    

      

    ----------------
    [zhaoxin第1天的登录状态YES, zhaoxin第2天的登录状态NO, zhaoxin第3天的登录状态YES, zhaoxin第4天的登录状态NO]

    一对多外键关联也是异曲同工,但是依然要注意几个细节:
    在关联时,我们都知道了是类似这样的stu_id = Column(Integer, ForeignKey('student.id')),那么这样一对多代表着这样的代码不止一行,关联的键不一样;
    那么有几条这个,就有几条类似student = relationship('Student',backref='my_study_recode')的代码,这个时候要指定
    foreign_keys = [***这里是本对象下关联外键的属性名***],如此一来计算机就能识别到底谁关联了谁,就不会分不清,如果把这个写进函数,那么计算机就会混淆,然后报错:大概说的就是有多个foreign_keys关联在这个表里,foreign_keys这个属性应该指定,否则会傻傻分不清楚。

    好了,下面最后讲下最重要的多对多关联

    直接在代码里面讲解,重新创建新表。(为了演示效果,我并未分开放,作为合格的开发人员,创建和修改查询等文件写在一个代码里实为不妥,但是这里是演示,就写一起了。)

    import sqlalchemy
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import create_engine,Table
    from sqlalchemy import Column,Integer,String,ForeignKey
    from sqlalchemy.orm import sessionmaker,relationship
    
    engine=create_engine('mysql+pymysql://root:123456@localhost/slkdb?charset=utf8')
    Base = declarative_base()
    book_m2m_author =Table('book_m2m_author',Base.metadata,
                           Column('book_id',Integer,ForeignKey('book.id')),
                           Column('author_id',Integer,ForeignKey('author.id'))
                           )#为什么不用class的方式创建这个表呢?因为我不会在里面插任何数据或者更改,我只需要改源数据就是那两个class里面的,那么这个由映射都会自动更改
    
    class Book(Base):
        __tablename__='book'
        id=Column(Integer,primary_key=True)
        name = Column(String(32))
        put_date = Column(String(32))
        authors = relationship('Author',secondary=book_m2m_author,backref='books')#连接author表,查询时通过第三张表book_m2m_author查询
        def __repr__(self):
            return self.name
    
    class Author(Base):
        __tablename__='author'
        id = Column(Integer, primary_key=True)
        name = name = Column(String(32))
        def __repr__(self):
            return self.name
    
    Base.metadata.create_all(engine)
    
    
    session_class=sessionmaker(bind=engine)
    session=session_class()
    
    b1=Book(name='圣墟+琴帝',put_date='2016-6-12')
    b2=Book(name='长生界+我欲封天',put_date='2013-2-19')
    b3=Book(name='神墓+求魔',put_date='2009-10-11')
    b4=Book(name='仙逆+一念永恒',put_date='2011-6-12')
    b5=Book(name='斗罗大陆+遮天',put_date='2010-8-2')
    b6=Book(name='三合一',put_date='2018-3-7')
    
    a1=Author(name='辰东')
    a2=Author(name='耳根')
    a3=Author(name='唐家三少')
    
    b1.authors=[a1,a3]#第三个表通过这个方式关联起author和book这两个表格,以下同。就是Book类中authors里的secondary这个属性的实现。
    b2.authors=[a1,a2]
    b3.authors=[a1,a2]
    b4.authors=[a2,a3]
    b5.authors=[a3,a1]
    b6.authors=[a1,a2,a3]
    session.add_all([b1,b2,b3,b4,b5,b6,a1,a2,a3])
    session.commit()
    
    author_obj = session.query(Author).filter(Author.name=='辰东').first()
    print(author_obj.books)#通过book实现到Book里面调用匹配辰东属性的反查效果
    book_obj = session.query(Book).filter(Book.name=='三合一').first()
    print(book_obj,book_obj.authors)#如此就实现作者查书 和书查作者的关联
    

      

    ---------------------------------
    输出:
    [圣墟+琴帝, 长生界+我欲封天, 神墓+求魔, 斗罗大陆+遮天, 三合一]
    三合一 [辰东, 耳根, 唐家三少]


    好了,到这里orm具体也讲完了,实际生活中,多表格关联会很多,如果您看的一脸懵逼,那对不起,可能我自己水平还不高,无法讲解入微,人生苦短,我用python。

  • 相关阅读:
    012——matlab判断变量是否存在
    011——MATLAB清除工作控件变量
    014——C#新建文件夹
    征服django的关键内容
    Django中session的使用
    RabbitMq与Redis的使用
    python的命名规则
    python类的深层次理解
    python类总结
    python之paramiko模块
  • 原文地址:https://www.cnblogs.com/Jason504327775/p/8522224.html
Copyright © 2020-2023  润新知