SQLALchemy
SQLALchemy
SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作 ,也就是将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
安装:pip3 install sqlalchemy
组成部分:
- Engine,框架的引擎
- Connection Pooling ,数据库连接池
- Dialect,选择连接数据库的DB API种类
- Schema/Types,架构和类型
- SQL Exprression Language,SQL表达式语言
SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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 |
问题:SQLALchemy,ORM框架
1、什么是orm框架
orm:对象关系映射
类 --- 表
对象 --- 一条记录
当有了对应关系之后,不需要再编写sql语句,直接操作,类,对象
2、sql vs orm
sql查询速度快,开发效率低
orm开发效率高,查询速度较低
3、概念理解
-db first 根据数据库的表生成类
-code first 根据类生成数据库的表
4、orm是怎么实现的
根据对象和类通过字符串格式化转化成sql语句
DDD中:unit of work
使用原生sql语句
import time import threading import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.engine.base import Engine engine = create_engine( "mysql+pymysql://root:123@127.0.0.1:3306/t1?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) def task(arg): conn = engine.raw_connection() cursor = conn.cursor() cursor.execute( "select * from t1" ) result = cursor.fetchall() cursor.close() conn.close() for i in range(20): t = threading.Thread(target=task, args=(i,)) t.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import threading import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.engine.base import Engine engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5) def task(arg): conn = engine.contextual_connect() with conn: cur = conn.execute( "select * from t1" ) result = cur.fetchall() print(result) for i in range(20): t = threading.Thread(target=task, args=(i,)) t.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import threading import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.engine.base import Engine from sqlalchemy.engine.result import ResultProxy engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5) def task(arg): cur = engine.execute("select * from t1") result = cur.fetchall() cur.close() print(result) for i in range(20): t = threading.Thread(target=task, args=(i,)) t.start()
使用orm语句
创建数据库表
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, DateTime,ForeignKey,UniqueConstraint from sqlalchemy import create_engine from sqlalchemy.orm import relationship import datetime Base = declarative_base() # 创建单表 class Classes(Base): __tablename__ = 'classes' id = Column(Integer, primary_key=True,autoincrement=True) name = Column(String(32)) class Student(Base): __tablename__="student" id =Column(Integer,primary_key=True,autoincrement=True) user=Column(String(32)) pwd=Column(String(32)) ctime=Column(DateTime,default=datetime.datetime.now) #不要加() class_id=Column(Integer,ForeignKey("classes.id")) #不会生成字段,用于链表查询,backref用于反向链表查表 cls = relationship("Classes", backref='stus') class Hobby(Base): __tablename__="hobby" id=Column(Integer,primary_key=True,autoincrement=True) caption=Column(String(32),default="大球") # 多对多关系 需要自检创建第三张表 class StudentToHobby(Base): __tablename__="studenttohobby" id = Column(Integer, primary_key=True, autoincrement=True) student_id=Column(Integer,ForeignKey('student.id')) hobby_id=Column(Integer,ForeignKey('hobby.id')) # 联合唯一 __table_args__=( UniqueConstraint("student_id","hobby_id",name="uix_student_id_hobby_id"), ) def init_db(): # 数据库连接相关 engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") # 创建表 Base.metadata.create_all(engine) def drop_db(): engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") # 删除表 Base.metadata.drop_all(engine) if __name__ == '__main__': init_db() # drop_db()
增
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import models from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine # 连接数据库 engine=create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") Session=sessionmaker(bind=engine) # 每次执行数据库操作时,都需要创建一个session session=Session() # 添加单条数据 # obj=models.Classes(name="全栈8期") # session.add(obj) # 添加多条数据 # obj_all=[ # models.Classes(name="全栈9期"), # models.Classes(name="全栈10期"), # models.Classes(name="全栈11期") # ] # session.add_all(obj_all) # 添加数据(含有外键) obj=models.Student(user="小花",pwd="123",class_id=2) session.add(obj) # 提交事务 session.commit() # 关闭session session.close()
查
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import models from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine,text # 连接数据库 engine=create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") Session=sessionmaker(bind=engine) # 每次执行数据库操作时,都需要创建一个session session=Session() # 查全部数据 # class_list=session.query(models.Classes).all() # for classes in class_list: # print(classes.id,classes.name) #别名查询 # class_list=session.query(models.Classes.id,models.Classes.name.label("xx")).all() # for classes in class_list: # print(classes.id,classes.xx) #条件查询 filter(条件) filter_by() # obj=session.query(models.Classes).filter(models.Classes.name=="全栈9期").all() # for i in obj: # print(i.name) # obj1=session.query(models.Classes).filter(models.Classes.name=="全栈9期").first() # print(obj1.name) # obj2=session.query(models.Classes).filter_by(name="全栈10期").first() # print(obj2.name) #子查询 # result = session.query(models.Classes).from_statement(text("SELECT * FROM classes where name=:name")).params(name='全栈11期').all() # print(result) # ret = session.query(models.Classes).filter(models.Classes.id.in_(session.query(models.Classes.id).filter_by(name='全栈8期'))).all() # print(ret) # 显示所有学生信息(含班级) # 1、多次查询 # obj=session.query(models.Student).all() # for obj in objs: # cls_obj = session.query(models.Classes).filter(models.Classes.id==obj.class_id).first() # print(obj.id,obj.username,obj.class_id,cls_obj.name) # 2、连表查询 isouter=True mysql中leftjoin查询 # objs = session.query(models.Student.id,models.Student.user,models.Classes.name).join(models.Classes,isouter=True).all() # print(objs) # 3、使用relationship查询 # objs = session.query(models.Student).all() # for item in objs: # print(item.id,item.user,item.class_id,item.cls.name) #4、使用relationship反向查询 全栈9期所有的学生 # obj = session.query(models.Classes).filter(models.Classes.name=='全栈9期').first() # student_list = obj.stus # for item in student_list: # print(item.id,item.user) # 提交事务 session.commit() # 关闭session session.close()
改
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import models from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine # 连接数据库 engine=create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") Session=sessionmaker(bind=engine) # 每次执行数据库操作时,都需要创建一个session session=Session() # 改 # session.query(models.Classes).filter(models.Classes.id > 1).update({"name" : "099"}) # synchronize_session=False 字符串拼接 # session.query(models.Classes).filter(models.Classes.id > 0).update({models.Classes.name: models.Classes.name + "099"}, synchronize_session=False) # synchronize_session="evaluate" 数字计算 session.query(models.Classes).filter(models.Classes.id > 0).update({"age": models.Classes.age + 1}, synchronize_session="evaluate") # 提交事务 session.commit() # 关闭session session.close()
删
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import models from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine # 连接数据库 engine=create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8sql?charset=utf8") Session=sessionmaker(bind=engine) # 每次执行数据库操作时,都需要创建一个session session=Session() # 删 session.query(models.Classes).filter(models.Classes.id > 2).delete() # 提交事务 session.commit() # 关闭session session.close()
补充操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 条件 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() #between区间 ret = session.query(Users).filter(Users.id.in_([1,3,4])).all() #id=1or3or 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() # 限制 (mysql中的limit) 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() #inner join 一张表没有的,都不显示 ret = session.query(Person).join(Favor, isouter=True).all() #left join 以左表为基准 # 组合 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() #显示数据重复
relationship
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
- relationship作用?增加数据和查询 一对多: class UserType(Base): __tablename__ = 'usertype' id = Column(Integer, primary_key=True) caption = Column(String(50), default='管理员') class Person(Base): __tablename__ = 'person' nid = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=True) hobby_id = Column(Integer, ForeignKey("usertype.id")) # 与生成表结构无关,仅用于查询方便 hobby = relationship("UserType", backref='pers') 数据添加: session.add(UserType(caption='超级管理员')) session.commit() session.add(Person(name='翔龙',hobby_id=1)) session.commit() user表添加数据,同时也给UserType表添加数据 session.add(Person(name='小韩',hobby=UserType(cation='VVIP'))) session.commit() 数据查询: 正向操作: obj = session.query(Person).filter(Person.nid==2).first() obj.hobby.caption 反向操作: obj = session.query(UserType).filter(UserType.id==1).first() obj.pers 多对多: class User2Hobby(Base): __tablename__ = 'user2hobby' id = Column(Integer, primary_key=True, autoincrement=True) hobby_id = Column(Integer, ForeignKey('hobby.id')) user_id = Column(Integer, ForeignKey('user.id')) class Hobby(Base): __tablename__ = 'hobby' id = Column(Integer, primary_key=True) title = Column(String(64), unique=True, nullable=False) # 与生成表结构无关,仅用于查询方便 users = relationship('User', secondary='user2hobby', backref='hbs') class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(64), unique=True, nullable=False) 添加数据: 添加单条: session.add(Hobby(title='篮球')) session.commit() session.add(User(name='梅凯')) session.commit() session.add(User2Hobby(hobby_id=1,user_id=1)) session.commit() 添加多条 正向: obj = Hobby(title='篮球') obj.servers = [User(name='王岩'),User(name='晓梅')] session.add(obj) session.commit() 反向: obj = User(title='俊杰') obj.hbs = [Hobby(title='翔龙'),Hobby(title='兴隆')] session.add(obj) session.commit() 查询: 反向: obj = session.query(User).filter(User.id==2).first() obj.hbs 正向: obj = session.query(Hobby).filter(Hobby.id==2).first() obj.users
创建session的两种方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import threading 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 from db import Users engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) def task(arg): session = Session() obj1 = Users(name="alex1") session.add(obj1) session.commit() for i in range(10): t = threading.Thread(target=task, args=(i,)) t.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session from models import Users engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) """ # 线程安全,基于本地线程实现每个线程用同一个session # 特殊的:scoped_session中有原来方法的Session中的一下方法: public_methods = ( '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', 'close', 'commit', 'connection', 'delete', 'execute', 'expire', 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind', 'is_modified', 'bulk_save_objects', 'bulk_insert_mappings', 'bulk_update_mappings', 'merge', 'query', 'refresh', 'rollback', 'scalar' ) """ session = scoped_session(Session) # ############# 执行ORM操作 ############# obj1 = Users(name="alex1") session.add(obj1) # 提交事务 session.commit() # 关闭session session.close()
flask-session默认也是使用的第二种方式:scoped_session
其它
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
- SQL a. select * from A where id in (select id from B) #不能使用*必须与id类型匹配 b. #1表示常量,每条数据都添加1 select id, name, 1 from A select id, name, 1, (select max(id) from B) as b from A select id, name, 1, (select max(id) from B where B.xid=A.id) as b from A +----+---------------+ | id | name | +----+---------------+ | 1 | 全栈1期099 | | 2 | 全栈2期099 | +----+---------------+ +----+---------+ | id | caption | xid +----+---------+ | 1 | 篮球 | 1 | 2 | 球 | 1 +----+---------+ subqry = session.query(func.count(Server.id).label("sid")).filter(Server.id == Group.id).correlate(Group).as_scalar() result = session.query(Group.name, subqry) """ SELECT `group`.name AS group_name, (SELECT count(server.id) AS sid FROM server WHERE server.id = `group`.id) AS anon_1 FROM `group` """ # 也可以使用原生SQL """ # 查询 cursor = session.execute('select * from users') result = cursor.fetchall() # 添加 cursor = session.execute('insert into users(name) values(:value)',params={"value":'wupeiqi'}) session.commit() print(cursor.lastrowid) """