ORM简介
orm英文全称object relational mapping,就是对象映射关系程序;在面向对象编程中,程序认为一切皆对象,但是我们数据库却是关系型的,为了保证这种一致性,我们通过ORM将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不是使用sql语句,如下图:
ORM的优点
ORM的核心是隐藏了数据访问细节,“封闭”通用数据库交互;是通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句; ORM使我们构造固话数据结构变得简单易行。
ORM的缺点
自动化意味着映射和关联管理,代价是牺牲性能; 各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad, Cache),效果很显著。
Python中常用的ORM架构
https://www.cnblogs.com/dingjiaoyang/p/11039359.html
sqlalchemy基本介绍
在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表,官网戳这里。
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...]
engine = create_engine("mysql+pymysql://root:123456@localhost:3306/db4?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) echo = True # echo参数为True时,会显示每条执行的SQL语句,可以关闭 ", max_overflow = 5)
更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=5) # 执行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES ('1.1.1.22', 3)" # ) # 新插入行自增ID # cur.lastrowid # 执行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES(%s, %s)",[('1.1.1.22', 3),('1.1.1.221', 3),] # ) # 执行SQL # cur = engine.execute( # "INSERT INTO hosts (host, color_id) VALUES (%(host)s, %(color_id)s)", # host='1.1.1.99', color_id=3 # ) # 执行SQL # cur = engine.execute('select * from hosts') # 获取第一行数据 # cur.fetchone() # 获取第n行数据 # cur.fetchmany(3) # 获取所有数据 # cur.fetchall()
sqlalchemy使用
主要包含安装、新建表、插入数据、查询数据、修改数据、删除数据,其他一些操作
安装sqalchemy
pip install pymysql pip install MySQLClient pip install SQLAlchemy
新建表及插入数据
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker # 需要单独导入 # echo=True,将操作过程打印出来 # 建立engine,为LAZY 模式,即此时未连接数据库,直到真正需要操作数据库时才连接 engine = create_engine("mysql+mysqldb://root:123.com@localhost/fcdb", 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) # 创建表结构 #### 新建数据 ## # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 Session_class = sessionmaker(bind=engine) # 生成session实例 Session = Session_class() # 生成你要创建的数据对象 user_obj = User(name="Burce", password="123") print(user_obj.name, user_obj.id)# 此时还没创建对象呢,不信你打印一下id发现还是None # 把要创建的数据对象添加到这个session里, 一会统一创建 Session.add(user_obj) print(user_obj.name, user_obj.id) # 此时也依然还没创建 Session.commit() # 现此才统一提交,创建数据 print(user_obj.name, user_obj.id) # 注如果要新增多个多项可以使用 Session.add_all([table_obj1, table_obj2])
另外一种不太常用的新建表的方法
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) # 使用mapper方法建立,User类与user表的映射
查询数据
基本的查询
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker # 需要单独导入 # echo=True,将操作过程打印出来 engine = create_engine("mysql+mysqldb://root:123.com@localhost/fcdb", encoding='utf-8', echo=False) Base = declarative_base() # 生成orm基类 class User(Base): # 类的映射仍然要存在 __tablename__ = 'user' # 表名 id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) # 显示User实例时,格式化 def __repr__(self): return "<ID(%s) NAME(%s)>" % (self.id, self.name) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 Session_class = sessionmaker(bind=engine) # 生成session实例 Session = Session_class() # 查询-使用filter_by查询 my_user = Session.query(User).filter_by(name="Burce").all() print(my_user) # 查询-使用filter查询 my_user1 = Session.query(User).filter(User.name == "Burce").all() print(my_user1)
filter_by与filter的区别
模块 | 语法 | ><(大于和小于查询) | and_和or_查询 |
fliter_by() | 直接用属性名,比较用= | 不支持 | 不支持,但可以直接括号内带多条件 |
filter() | 用类名.属性名,比较用== | 支持 | 支持<无法使用> |
# filter apply the given filtering criterion to a copy of this Query, using SQL expressions. session.query(MyClass).filter(MyClass.name == 'some name') # filter_by apply the given filtering criterion to a copy of this Query, using keyword expressions. session.query(MyClass).filter_by(name = 'some name') # filter及filter_by的区别主要体现在语法上,filter_by无法使用 > <查询; q = sess.query(IS).filter(IS.node == node).filter(IS.password == password).all() q = sess.query(IS).filter(IS.node == node, IS.password == password).filter().all() q = sess.query(IS).filter_by(node=node, password=password).all() query.filter(User.name=='ed') query.filter(User.name!='ed') query.filter(User.name.like('%ed%')) query.filter(User.name.in_(['ed','wen','jask'])) query.filter(~User.name.in_(['ed','wen','jask'])) query.filter(User.name==None) query.filter(User.name!=None) query.filter(and_(User.name=='ed',Username=='ed jones')) # 貌似并不支持 query.filter(or_(User.name= 'ed',User.name=='wendy') # 貌似并不支持 query.filter(User.name.match('wendy')) # 使用特定于数据库的匹配或包含函数;它的行为会随着后端而变化
filter及filter_by的返回
all() # 返回一个列表 first() # 返回至多一个结果,而且以单项形式,而不是只有一个元素的tuple形式返回这个结果 one() # 返回且仅返回一个查询结果。当结果的数量不足一个或者多于一个时会报错 one_or_one() # 当结果数量为0时返回None, 多于1个时报错 # 没有这个 scalar() # 成功则返回该行的第一列的列号 count() # 返回符合条件的总数 # text() 如下: >>> from sqlalchemy import text sql>>> for user in session.query(User). ... filter(text("id<224")). ... order_by(text("id")).all(): ... print(user.name) ed wendy mary # params():传递参数 >>> session.query(User).filter(text("id<:value and name=:name")). ... params(value=224, name='fred').order_by(User.id).one() <User(name='fred', fullname='Fred Flinstone', password='blah')> # from_statement():直接使用完整的SQL语句,但是要注意将表名和列名写正确 >>> session.query(User).from_statement( ... text("SELECT * FROM users where name=:name")). ... params(name='ed').all() [<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>] # func_count():可以直接指出要测次数的某一项 >>> from sqlalchemy import func sql>>> session.query(func.count(User.name), User.name).group_by(User.name).all() [(1, u'ed'), (1, u'fred'), (1, u'mary'), (1, u'wendy')]
查询例子
ret = session.query(Users).all() ret = session.query(Users.name, Users.extra).all() ret = session.query(Users).filter_by(name='111').all() ret = session.query(Users).filter_by(name='111').first() ret = session.query(Users).filter(text("id<:value and name=:name")).params(value=224, name='fred').order_by(User.id).all() ret = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='ed').all()
修改数据
# 修改->查询出的条目->直接修改 my_user1 = Session.query(User).filter(User.name == "Burce").filter(User.id > 1).first() my_user1.name = "Bruce" Session.commit() # 提交写库 print(my_user1.name, my_user1.password) # 修改->update批量修改 session.query(Users).filter(Users.id > 2).update({"name" : "099"}) session.query(Users).filter(Users.id > 2).update({Users.name: Users.name + "099"}, synchronize_session=False) session.query(Users).filter(Users.id > 2).update({"num": Users.num + 1}, synchronize_session="evaluate") session.commit()
删除数据
# 删除-->query删除匹配的所有记录,返回删除的条目数 line_counts = Session.query(User).filter(User.name == "Burce").delete() print(line_counts) Session.commit()
修改表结构
def add_column(engine, tableName, column): columnName = column.compile(dialect=engine.dialect) print("column Name>>",columnName) columnType = column.type.compile(engine.dialect) print("column Type>>", columnType) engine.execute('ALTER TABLE %s ADD COLUMN %s %s' % ( tableName, columnName, columnType)) def drop_colunm(engine, tableName, column): columnName = column.compile(dialect=engine.dialect) print("column Name>>",columnName) columnType = column.type.compile(engine.dialect) print("column Type>>", columnType) engine.execute('ALTER TABLE %s DROP COLUMN %s ' % ( tableName, columnName)) column = Column('count', Integer, default='0') add_column(engine, "book", column) drop_colunm(engine, "book", column)
回滚操作
from sqlalchemy import create_engine from sqlalchemy import Column, Integer, String, Enum from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker engine = create_engine("mysql+pymysql://root:123.com@localhost/fcdb", encoding='utf-8', echo=False) # 生成ORM基类 Base = declarative_base() class MyFamily(Base): __tablename__ = 'myfamily' # 表名 serialNum = Column(Integer, primary_key=True) name = Column(String(32)) age = Column(Integer) role = Column(String(32)) atHome = Column(Enum("Yes", "No")) def __repr__(self): return "<%s>" % self.name #Base.metadata.create_all(engine) Session_class = sessionmaker(bind=engine) Session = Session_class() graMu = MyFamily(name="R Zhu", age=2, role="GrandMum", atHome="Yes") Session.add(graMu) print(Session.query(MyFamily).filter(MyFamily.name == "R Zhu").all()) Session.rollback() print(Session.query(MyFamily).filter(MyFamily.name == "R Zhu").all()) # 返回结果<回滚成功,那么操作数据库了么?> [<R Zhu>] [] # 在cli下对myfamily新建一条目-自增的序号显示,已经被操作过2次 mysql> insert into myfamily (name, age, role, atHome) values ("R Zhu", 50, "GrandMum", "Yes"); Query OK, 1 row affected (0.11 sec) mysql> select * from myfamily; +-----------+-----------+------+----------+--------+ | serialNum | name | age | role | atHome | +-----------+-----------+------+----------+--------+ | 1 | Bruce Lee | 29 | Husband | No | | 2 | Alvin Lee | 2 | Son | Yes | | 3 | Min Lee | 25 | Wife | No | | 6 | R Zhu | 50 | GrandMum | Yes | +-----------+-----------+------+----------+--------+ 4 rows in set (0.00 sec)
统计与分组
from sqlalchemy import func result = Session.query(MyFamily, func.count(MyFamily.name)).group_by(MyFamily.name).all() print(result) [(<Bruce Lee>, 1), (<Alvin Lee>, 1), (<Min Lee>, 1), (<R Zhu>, 2)] # 相当于SQL语句 SELECT count(myfamily.name) AS count_1, myfamily.name AS myfamily_name FROM myfamily GROUP BY myfamily.name
注: mysql报错解决办法:https://blog.csdn.net/qq_33621326/article/details/95950264
其他一些操作参考
# 条件 ret = session.query(Users).filter_by(name='alex').all() ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all() ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all() ret = session.query(Users).filter(Users.id.in_([1,3,4])).all() ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all() ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='eric'))).all() from sqlalchemy import and_, or_ ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all() ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all() ret = session.query(Users).filter( or_( Users.id < 2, and_(Users.name == 'eric', Users.id > 3), Users.extra != "" )).all() # 通配符 ret = session.query(Users).filter(Users.name.like('e%')).all() ret = session.query(Users).filter(~Users.name.like('e%')).all() # 限制 ret = session.query(Users)[1:2] # 排序 ret = session.query(Users).order_by(Users.name.desc()).all() ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all() # 分组 from sqlalchemy.sql import func ret = session.query(Users).group_by(Users.extra).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all() # 连表 # 任何情况下都可以使用 ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all() # 必须在有外键的情况下使用 ret = session.query(Person).join(Favor).all() # Left join与 right join区别,同样需要在有外键的情况下尝试 ret = session.query(Person).join(Favor, isouter=True).all() # 组合 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union(q2).all() q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union_all(q2).all()
对外键的相关设定与操作
The relationship.back_populates
parameter is a newer version of a very common SQLAlchemy feature calledrelationship.backref
. The relationship.backref
parameter hasn’t gone anywhere and will always remain available! The relationship.back_populates
is the same thing, except a little more verbose and easier to manipulate. For an overview of the entire topic, see the section Linking Relationships with Backref.
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=5) Base = declarative_base() # 创建单表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'),[为约束,间注] Index('ix_id_name', 'name', 'extra'), ) def __repr__(self): return "%s-%s" %(self.id, self.name) # 一对多 class Favor(Base): __tablename__ = 'favor' nid = Column(Integer, primary_key=True) caption = Column(String(50), default='red', unique=True) def __repr__(self): return "%s-%s" %(self.nid, self.caption) class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) favor_id = Column(Integer, ForeignKey("favor.nid")) # 与生成表结构无关,仅用于查询方便,仅在内存中; # 让Favor表的对象,可以通过obj.pers的方式反查Person表中的所有字段 # 同时让Person表对象,可以通过Favor直接调用Favor表中的所有字段 # 一个爱好,可以对应多个人,一个人只有一个爱好; # So Person->查-只有一条记录; # Faver查person可以查出很多条,所以即1对多的外键映射关系; favor = relationship("Favor", backref='pers') # 多对多 class ServerToGroup(Base): __tablename__ = 'servertogroup' nid = Column(Integer, primary_key=True, autoincrement=True) server_id = Column(Integer, ForeignKey('server.id')) group_id = Column(Integer, ForeignKey('group.id')) group = relationship("Group", backref='s2g') server = relationship("Server", backref='s2g') class Group(Base): __tablename__ = 'group' id = Column(Integer, primary_key=True) name = Column(String(64), unique=True, nullable=False) port = Column(Integer, default=22) # group = relationship('Group',secondary=ServerToGroup,backref='host_list') class Server(Base): __tablename__ = 'server' id = Column(Integer, primary_key=True, autoincrement=True) hostname = Column(String(64), unique=True, nullable=False) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) Session = sessionmaker(bind=engine) session = Session() 注:https://blog.csdn.net/w_linux/article/details/79655073
多对多外键无法分清的情况:
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+mysqldb://burce:123@192.168.0.104:3306/mydb?charset=utf8", echo=True) Base = declarative_base() Session_class = sessionmaker(engine) session = Session_class() class Customer(Base): __tablename__ = 'customer' id = Column(Integer, primary_key=True) name = Column(String(32)) billing_address_id = Column(Integer, ForeignKey("address.id")) shipping_address_id = Column(Integer, ForeignKey("address.id")) # 首先billing_address是Customer对象指向Address对象的属性; # 多foreignKey的情况下,必须为relationship指定外键,如果不指定,那么Customer.billing_address调用的哪个外键呢? # 在backref的时候,也同样需要两个不同的名称来指定调用的是哪个relationship billing_address = relationship("Address", foreign_keys=[billing_address_id], backref="cm1") shipping_address = relationship("Address", foreign_keys=[shipping_address_id], backref="cm2") class Address(Base): __tablename__ = 'address' id = Column(Integer, primary_key=True) street = Column(String(32)) city = Column(String(32)) state = Column(String(32)) Base.metadata.drop_all(engine) Base.metadata.create_all(engine) addre1 = Address(id="1", street="street1", city="city1", state="state1") cus1 = Customer(id="1001", name="Bruce", billing_address_id="1", shipping_address_id="1") cus2 = Customer(id="1002", name="Alvin", billing_address_id="1", shipping_address_id="1") session.add_all([addre1, cus1, cus2]) print(session.query(Customer).first().billing_address.street) print(session.query(Address).first().cm1) session.commit()
多对多:
现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是
- 一本书可以有好几个作者一起出版
- 一个作者可以写好几本书
此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为
当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low b了,改书名还得都改。。
那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了
这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联
用orm如何表示呢?
#一本书可以有多个作者,一个作者又可以出版多本书 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这张表示不需要用户来维护的 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)
# 在创建关系时,因为第三张表的原因,author与Book是互相不可见的,通过下面一条命令,在ORM中建立关系,从而完成互相的调用; 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="跟Alex学Python") b2 = Book(name="跟Alex学把妹") b3 = Book(name="跟Alex学装逼") b4 = Book(name="跟Alex学开车") 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 | 跟Alex学Python | NULL | | 2 | 跟Alex学把妹 | NULL | | 3 | 跟Alex学装逼 | NULL | | 4 | 跟Alex学开车 | 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="跟Alex学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()
-
-
-
-
-
-
-
-
通过书表查关联的作者
-
-
-
-
-
-
-
-
-
跟Alex学Python [Alex, Jack]
-
-
-
-
-
-
-
-
通过作者表查关联的书
-
-
-
-
-
-
-
-
-
Alex [跟Alex学把妹, 跟Alex学Python]
多对多删除
删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除
通过书删除作者
author_obj =s.query(Author).filter_by(name="Jack").first() book_obj = s.query(Book).filter_by(name="跟Alex学把妹").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) 写中文在字符串后面加?charset=utf8,encoding=’utf-8’不生效
end