美文网首页
pymysql与SQLAchemy的基本知识点整理

pymysql与SQLAchemy的基本知识点整理

作者: NewForMe | 来源:发表于2018-10-24 11:31 被阅读0次

    本篇对于Python操作MySQL主要使用两种方式:

    • 原生模块 pymsql
    • ORM框架 SQLAchemy

    一、pymysql

      1. 下载安装
        pip install pymysql
    • 2.使用操作
      ------1.执行SQL
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
      
    # 创建连接
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    # 创建游标
    cursor = conn.cursor()
    # 执行SQL,并返回收影响行数
    effect_row = cursor.execute("update hosts set host = '1.1.1.2'")
    # 执行SQL,并返回受影响行数
    #effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
    # 执行SQL,并返回受影响行数
    #effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
    # 提交,不然无法保存新建或者修改的数据
    conn.commit()
    # 关闭游标
    cursor.close()
    # 关闭连接
    conn.close()
    

    ------2.获取新创建数据自增ID

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
      
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
    conn.commit()
    cursor.close()
    conn.close()
      
    # 获取最新自增ID
    new_id = cursor.lastrowid
    

    ------3.获取查询数据

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
      
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.execute("select * from hosts")
      
    # 获取第一行数据
    row_1 = cursor.fetchone()
      
    # 获取前n行数据
    # row_2 = cursor.fetchmany(3)
    # 获取所有数据
    # row_3 = cursor.fetchall()
      
    conn.commit()
    cursor.close()
    conn.close()
    

    注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:

    • cursor.scroll(1,mode='relative') # 相对当前位置移动
    • cursor.scroll(2,mode='absolute') # 相对绝对位置移动
      ------ 4.fetch数据类型
      关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
      
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    # 游标设置为字典类型
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    r = cursor.execute("call p1()")
      
    result = cursor.fetchone()
      
    conn.commit()
    cursor.close()
    conn.close()
    

    二、SQLAchemy

    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果
    安装:pip install SQLAlchemy

    image.png

    SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,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
    

    1、SQLAchemy的基本使用

    • 创建表
    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String
     
    engine = create_engine("mysql+pymysql://root:123456@localhost/testdb",
                                        encoding='utf-8', echo=True)
     
     
    Base = declarative_base() #生成orm基类
     
    class User(Base):
        __tablename__ = 'user' #表名
        id = Column(Integer, primary_key=True)
        name = Column(String(32))
        password = Column(String(64))
     
    Base.metadata.create_all(engine) #创建表结构
    

    除上面的创建之外,还有一种创建表的方式,虽不常用,但还是看看吧

    from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey
    from sqlalchemy.orm import mapper
     
    metadata = MetaData()
     
    user = Table('user', metadata,
                Column('id', Integer, primary_key=True),
                Column('name', String(50)),
                Column('fullname', String(50)),
                Column('password', String(12))
            )
     
    class User(object):
        def __init__(self, name, fullname, password):
            self.name = name
            self.fullname = fullname
            self.password = password
     
    mapper(User, user) 
    #the table metadata is created separately with the Table construct,
     then associated with the User class via the mapper() function
    

    事实上,我们用第一种方式创建的表就是基于第2种方式的再封装。

    • 新增
    from sqlalchemy.orm import sessionmaker, relationship
    
    Session_class = sessionmaker(bind=engine) 
    #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
    Session = Session_class() #生成session实例
    
    user_obj = User(name="alex",password="123456") #生成你要创建的数据对象
    print(user_obj.name,user_obj.id)  #此时还没创建对象呢,不信你打印一下id发现还是None
     
    Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建
    print(user_obj.name,user_obj.id) #此时也依然还没创建
     
    Session.commit() #现此才统一提交,创建数据
    
    • 查询
    my_user = Session.query(User).filter_by(name="alex").first()
    #这样查询出来的不是直接的数据是一个对象
    print(my_user)#<__main__.User object at 0x105b4ba90>
    #所以再经一轮提取才能获得数据
    print(my_user.id,my_user.name,my_user.password)
    

    如果想查询出来直接是数据的话,可以通过修改类的定义来返回

    def __repr__(self):
        return "<User(name='%s',  password='%s')>" % (
            self.name, self.password)
    
    • 修改
      修改就是先查询出将要修改的内容,然后直接重新对其赋值,这样就能达到修改的目的。
    my_user = Session.query(User).filter_by(name="alex").first()
    my_user.name = "Alex Li" 
    Session.commit()
    
    • 回滚
    my_user = Session.query(User).filter_by(id=1).first()
    my_user.name = "Jack"
     
    fake_user = User(name='Rain', password='12345')
    Session.add(fake_user)
      #这时看session里有你刚添加和修改的数据
    print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) 
    #此时你rollback一下
    Session.rollback() 
    #再查就发现刚才添加的数据没有了。
    print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) 
    # Session
    # Session.commit()
    
    • 获取所有数据
      print(Session.query(User.name,User.id).all())
    • 多条件查询
      objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()
      上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果
    • 统计和分组
    #统计
    Session.query(User).filter(User.name.like("Ra%")).count()
    #分组
    from sqlalchemy import func
    print(Session.query(func.count(User.name),User.name).group_by(User.name).all() )
    #相当于原生sql为
    select count(user.name) AS count_1, user.name AS user_name
    FROM user GROUP BY user.name
    
    • 外键关联
      我们创建一个addresses表,跟user表关联
    from sqlalchemy import Column, Integer, String, ForeignKey
    from sqlalchemy.orm import relationship
     
    class Address(Base):
        __tablename__ = 'addresses'
        id = Column(Integer, primary_key=True)
        email_address = Column(String(32), nullable=False)
        user_id = Column(Integer, ForeignKey('user.id'))
     
        user = relationship("User", backref="addresses") 
    #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项
     
        def __repr__(self):
            return "<Address(email_address='%s')>" % self.email_address
    

    表创建好后,我们可以这样反查试试

    obj = Session.query(User).first()
    for i in obj.addresses: #通过user对象反查关联的addresses记录
        print(i)
     
    addr_obj = Session.query(Address).first()
    print(addr_obj.user.name)  #在addr_obj里直接查关联的user表
    

    创建关联对象

    obj = Session.query(User).filter(User.name=='rain').all()[0]
    print(obj.addresses)
     
    obj.addresses = [Address(email_address="r1@126.com"), #添加关联对象
                     Address(email_address="r2@126.com")]
    Session.commit()
    

    2、多外键关联
    下表中,Customer表有2个字段都关联了Address表

    from sqlalchemy import Integer, ForeignKey, String, Column
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import relationship
     
    Base = declarative_base()
     
    class Customer(Base):
        __tablename__ = 'customer'
        id = Column(Integer, primary_key=True)
        name = Column(String)
     
        billing_address_id = Column(Integer, ForeignKey("address.id"))
        shipping_address_id = Column(Integer, ForeignKey("address.id"))
     
        billing_address = relationship("Address") 
        shipping_address = relationship("Address")
     
    class Address(Base):
        __tablename__ = 'address'
        id = Column(Integer, primary_key=True)
        street = Column(String)
        city = Column(String)
        state = Column(String)
    

    创建表结构是没有问题的,但你Address表中插入数据时会报下面的错.

    sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join
    condition between parent/child tables on relationship
    Customer.billing_address - there are multiple foreign key
    paths linking the tables.  Specify the 'foreign_keys' argument,
    providing a list of those columns which should be
    counted as containing a foreign key reference to the parent table.
    

    解决办法如下:

    class Customer(Base):
        __tablename__ = 'customer'
        id = Column(Integer, primary_key=True)
        name = Column(String)
     
        billing_address_id = Column(Integer, ForeignKey("address.id"))
        shipping_address_id = Column(Integer, ForeignKey("address.id"))
     
        billing_address = relationship("Address", foreign_keys=[billing_address_id])
        shipping_address = relationship("Address", foreign_keys=[shipping_address_id])
    

    这样sqlachemy就能分清哪个外键是对应哪个字段了
    3、多对多关系
    现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是

    • 一本书可以有好几个作者一起出版
    • 一个作者可以写好几本书
    #一本书可以有多个作者,一个作者又可以出版多本书
    
    from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey
    from sqlalchemy.orm import relationship
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    
    
    Base = declarative_base()
    
    book_m2m_author = Table('book_m2m_author', Base.metadata,
                            Column('book_id',Integer,ForeignKey('books.id')),
                            Column('author_id',Integer,ForeignKey('authors.id')),
                            )
    
    class Book(Base):
        __tablename__ = 'books'
        id = Column(Integer,primary_key=True)
        name = Column(String(64))
        pub_date = Column(DATE)
        authors = relationship('Author',secondary=book_m2m_author,backref='books')
    
        def __repr__(self):
            return self.name
    
    class Author(Base):
        __tablename__ = 'authors'
        id = Column(Integer, primary_key=True)
        name = Column(String(32))
    
        def __repr__(self):
            return self.name
    

    接下来创建几本书和作者

    Session_class = sessionmaker(bind=engine) 
    #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
    s = Session_class() #生成session实例
     
    b1 = Book(name="Python入门到放弃")
    b2 = Book(name="精通Python72式")
    b3 = Book(name="MYSQL入门到装逼")
    b4 = Book(name="C#学习")
     
    a1 = Author(name="Alex")
    a2 = Author(name="Jack")
    a3 = Author(name="Rain")
     
    b1.authors = [a1,a2]
    b2.authors = [a1,a2,a3]
     
    s.add_all([b1,b2,b3,b4,a1,a2,a3])
     
    s.commit()
    

    此时,手动连上mysql,分别查看这3张表,你会发现,book_m2m_author中自动创建了多条纪录用来连接book和author表

    mysql> select * from books;
    +----+------------------+----------+
    | id | name             | pub_date |
    +----+------------------+----------+
    |  1 | Python入门到放弃   | NULL     |
    |  2 | 精通Python72式     | NULL     |
    |  3 | MYSQL入门到装逼     | NULL     |
    |  4 | C#学习     | NULL     |
    +----+------------------+----------+
    4 rows in set (0.00 sec)
     
    mysql> select * from authors;
    +----+------+
    | id | name |
    +----+------+
    | 10 | Alex |
    | 11 | Jack |
    | 12 | Rain |
    +----+------+
    3 rows in set (0.00 sec)
     
    mysql> select * from book_m2m_author;
    +---------+-----------+
    | book_id | author_id |
    +---------+-----------+
    |       2 |        10 |
    |       2 |        11 |
    |       2 |        12 |
    |       1 |        10 |
    |       1 |        11 |
    +---------+-----------+
    5 rows in set (0.00 sec)
    

    此时,我们去用orm查一下数据

    print('--------通过书表查关联的作者---------')
     
    book_obj = s.query(Book).filter_by(name="Python入门到放弃").first()
    print(book_obj.name, book_obj.authors)
     
    print('--------通过作者表查关联的书---------')
    author_obj =s.query(Author).filter_by(name="Alex").first()
    print(author_obj.name , author_obj.books)
    s.commit()
    

    输出如下:

    --------通过书表查关联的作者---------
    Python入门到放弃 [Alex, Jack]
    --------通过作者表查关联的书---------
    Alex [精通Python72式, Python入门到放弃]
    
    • 多对多删除
      删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除
    • 通过书删除作者
    author_obj =s.query(Author).filter_by(name="Jack").first()
     
    book_obj = s.query(Book).filter_by(name="精通Python72式").first()
     
    book_obj.authors.remove(author_obj) #从一本书里删除一个作者
    s.commit()
    
    • 直接删除作者
      删除作者时,会把这个作者跟所有书的关联关系数据也自动删除
    author_obj =s.query(Author).filter_by(name="Alex").first()
    # print(author_obj.name , author_obj.books)
    s.delete(author_obj)
    s.commit()
    
    • 处理中文
      sqlalchemy设置编码字符集一定要在数据库访问的URL上增加charset=utf8,否则数据库的连接就不是utf8的编码格式:
      eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)

    相关文章

      网友评论

          本文标题:pymysql与SQLAchemy的基本知识点整理

          本文链接:https://www.haomeiwen.com/subject/mclazftx.html