一、SQLAlchemy介绍和快速使用
1)介绍
SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
SQLAlchemy的组成部分有:
- Engine 框架的引擎
- Connection Pooling 数据库连接池
- Dialect 选择连接数据库的DB API种类
- Schema/Types 架构和类型
- SQL Exprression Language SQL表达式语言
安装SQLAlchemy
pip install sqlalchemy
SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
MySQL-Python
mysql + mysqldb: //<user>:<password>@<host>[:<port>]/<dbname>
("mysql+pymysql://root:123@127.0.0.1:3306/likes?charset=utf8")
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...]
2)原生操作快速使用
from threading import Thread # 第一步:导入包
from sqlalchemy import create_engine
engine = create_engine( # 第二步:实例化得到一个engine
"mysql+pymysql://root:123@127.0.0.1:3306/likes?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有,线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
def task(): # 第三步:通过engine拿到一个链接 拿到一个conn对象,从连接池中取出一个链接
conn = engine.raw_connection()
cursor = conn.cursor()
cursor.execute("select * from userinfo")
print(cursor.fetchall())
for i in range(20): # 第三步:多线程测试
t = Thread(target=task)
t.start()
二、创建操作数据表
1)创建数据表
# 写一个个类,继承某个父类,写字段
from sqlalchemy.ext.declarative import declarative_base # 第一步:导入一些依赖
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
import datetime
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base() # 第二步:创建一个父类
class User(Base): # 第三步:写类,继承父类
# 第四步:写字段,所有字段都是Column的对象,在里面通过参数控制类型
id = Column(Integer, primary_key=True) # id 主键
name = Column(String(32), index=True, nullable=False) # varchar32 index 带索引 nullable 不可为空
email = Column(String(32), unique=True) # 唯一
ctime = Column(DateTime, default=datetime.datetime.now) # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
extra = Column(Text, nullable=True)
__tablename__ = 'users' # 定义表名字 数据库表名称
__table_args__ = ( # 定义联合索引,联合唯一
UniqueConstraint('id', 'name', name='uix_id_name'), # 联合唯一
Index('ix_id_name', 'name', 'email'), # 联合索引
)
class Book(Base):
id = Column(Integer, primary_key=True) # id 主键
name = Column(String(32), index=True, nullable=False) # varchar32 name列,索引,不可为空
price = Column(Integer)
__tablename__ = 'books' # 数据库表名称
def __repr__(self):
return str(self.price)
class Publish(Base):
id = Column(Integer, primary_key=True) # id 主键
name = Column(String(32), nullable=True)
__tablename__ = 'publish' # 数据库表名称
"""
第5步:sqlalchemy没有迁移一说,只能创建出被Base管理的所有表,和删除被Base管理的所有表
sqlalchemy不能创建数据库,不能修改,删除字段,只能创建表,和删除表
"""
def init_db():
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa?charset=utf8", # 数据库没有需要手动创建
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Base.metadata.create_all(engine) # 创建出所有被Base管理的表
def drop_db():
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa?charset=utf8", # 数据库没有需要手动创建
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Base.metadata.drop_all(engine) # 删除所有被Base管理的表
if __name__ == '__main__':
init_db() # 创建使用这个
# drop_db() # 删除用这个
整体流程
#通过类 创建和删除表
-第一步:导入一些依赖
-第二步:创建成一个Base:Base = declarative_base()
-第三步:写类:都集成Base
class User(Base):
-第四步:写字段,字段都是Column类的对象,通过参数控制字段类型,是否可以为空,是否索引。。。
id = Column(Integer, primary_key=True) # id 主键
name = Column(String(32), index=True, nullable=False)
-第五步:定义表名,联合唯一,联合索引
__tablename__ = 'users' # 数据库表名称
__table_args__ = ( # 定义联合索引,联合唯一
UniqueConstraint('id', 'name', name='uix_id_name'), # 联合唯一
Index('ix_id_name', 'name', 'email'), # 联合索引
)
-第六步:把被Base管理的所有表,同步到数据库中[不能创建数据库,不能删除修改字段]
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Base.metadata.create_all(engine) # 创建出所有被Base管理的表
-第七步:删除被Base管理的所有表
# 删除所有被Base管理的表
Base.metadata.drop_all(engine)
2)SQLAlchemy快速插入数据
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import User,Book
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) # 第一步:创建engine
# 第二步:通过engine,获得session对象:跟之前学的cookie,session不是一个东西
Session = sessionmaker(bind=engine)
session = Session() # 每次执行数据库操作时,都需要创建一个Connection
# 第三步,通过session操作插入数据
# book=Book(name='开心每一天',price=33) # 如果已经插入过 再次插入新的内容记得注释掉已经插入过的内容
user=User(name='Like',email='7793@qq.com', extra='很帅')
session.add(user)
session.commit()
session.close()
三、Scoped_Session线程安全
1)线程安全
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import User, Book
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
"""
session是链接对象,如果集成到flask中,我们是把session定义成全局还是每个视图函数一个session呢?
正常来讲要每个视图函数定义一个session,有些麻烦。
sqlalchemy 帮咱提供了一个只要定义一次的session,能够做到在不同线程中,使用的是自己的session,底层基于local。
"""
from sqlalchemy.orm import scoped_session
from threading import Thread
# 原来
# session=Session() #不是线程安全
# 以后咱们使用这个它做到了线程安全
session = scoped_session(Session)
def task(i):
user = User(name='彭于晏%s' % i, email='%s@qq.com' % i, extra='很丑')
session.add(user)
session.commit()
session.close()
for i in range(50):
t = Thread(target=task, args=[i, ])
t.start()
2)类装饰器
# 类装饰器之:装饰器是类
class Auth():
def __init__(self, func):
self.func = func
def __call__(self, a, b):
print('我要开始加了')
res = self.func(a, b)
return res
@Auth # add=Auth(add)
def add(a, b): # add 是Auth的对象
return a + b
res = add(4, 5) # add加括号,会触发Auth的__call__
print(res)
文章来源:https://www.toymoban.com/news/detail-761530.html
函数装饰器文章来源地址https://www.toymoban.com/news/detail-761530.html
# 类装饰器之:加在类上的装饰器
def auth(func):
def inner(*args, **kwargs):
print("我要开始了")
res = func(*args, **kwargs)
res.name = 'Like'
return res
return inner
@auth # Person=auth(Person) # 加在类上的装饰器
class Person():
pass
p = Person()
print(p.name)
四、基本增删改查
1)基本增删查改和高级查询
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Boy, Girl, Boy2Girl, Hobby, Book, User, Person
from sqlalchemy.orm import scoped_session
from sqlalchemy.sql import text
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 1 基本增
# add add_all
# Hobby表增加记录
hobby=Hobby(caption='羽毛球')
book=Book(name='三国演义',price=22)
# # session.add(hobby)
session.add_all([hobby,book])
#
# 2 删除--->查询再删---》一般写项目都是软删除
hobby=session.query(Hobby).filter_by(caption='足球').delete()
print(hobby)
# 3 修改,更新
res=session.query(User).filter(User.id > 0).update({"name" : "lqz"})
# 类似于django的F查询
# 当字符串相加
res=session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False)
# 数字相加
res=session.query(Book).filter(Book.price > 20).update({"price": Book.price + 1}, synchronize_session="evaluate")
print(res)
# 4 查询: filer:写条件 filter_by:等于的值
# 4.1 查询所有 是list对象
res = session.query(User).all()
print(type(res))
print(len(res))
# 4.1.1 只查询某几个字段
# select name as xx,email from user;
res = session.query(User.name.label('xx'), User.email)
print(res) # 打出原生sql
print(res.all())
for item in res.all():
print(item[0])
# 4.1.2 filter传的是表达式,filter_by传的是参数
res = session.query(User).filter(User.name == "lqz").all()
res = session.query(User).filter(User.name != "lqz").all()
res = session.query(User).filter(User.name != "lqz", User.email == '47@qq.com').all()
res = session.query(User).filter_by(name='lqz099').all()
res = session.query(User).filter_by(name='lqz099',email='47@qq.com').all()
print(len(res))
# 4.2 取一个 all了后是list,list 没有first方法
res = session.query(User).first()
print(res)
# 4.3 查询所有,使用占位符(了解) :value :name
res = session.query(User).filter(text("id<:value and name=:name")).params(value=10, name='lqz099').order_by(User.id).all()
# 4.4 自定义查询(了解)
res=session.query(User).from_statement(text("SELECT * FROM users where email=:email")).params(email='7@qq.com').all()
print(res)
# 4.5 高级查询
# 条件
# 表达式,and条件连接
res = session.query(User).filter(User.id > 1, User.name == 'lqz099').all() # and条件
# between
res = session.query(User).filter(User.id.between(1, 3), User.name == 'lqz099').all()
# in
res = session.query(User).filter(User.id.in_([1,3,4])).all()
res = session.query(User).filter(User.email.in_(['y@qq.com','r@qq.com'])).all()
# ~非,除。。外
res = session.query(User).filter(~User.id.in_([1,3,4])).all()
# 二次筛选
res = session.query(User).filter(~User.id.in_(session.query(User.id).filter_by(name='lqz099'))).all()
# and or条件
from sqlalchemy import and_, or_
# or_包裹的都是or条件,and_包裹的都是and条件
res = session.query(User).filter(and_(User.id >= 3, User.name == 'lqz099')).all() # and条件
res = session.query(User).filter(or_(User.id < 2, User.name == 'eric')).all()
res = session.query(User).filter(
or_(
User.id < 2,
and_(User.name == 'lqz099', User.id > 3),
User.extra != ""
)).all()
# 通配符,以e开头,不以e开头
res = session.query(User).filter(User.email.like('%@%')).all()
res = session.query(User.id).filter(~User.name.like('e%'))
# 分页
# 一页2条,查第5页
res = session.query(User)[2*5:2*5+2]
# 排序,根据name降序排列(从大到小)
res = session.query(User).order_by(User.email.desc()).all()
res = session.query(Book).order_by(Book.price.desc()).all()
res = session.query(Book).order_by(Book.price.asc()).all()
# 第一个条件重复后,再按第二个条件升序排
ret = session.query(User).order_by(User.name.desc(), User.id.asc()).all()
# 分组查询
from sqlalchemy.sql import func
ret = session.query(User).group_by(User.extra).all()
# 分组之后取最大id,id之和,最小id
res = session.query(
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.extra).all()
print(res)
for item in res:
print(item[2])
# having
ret = session.query(
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.extra).having(func.max(User.id) > 2).all()
# 链表操作
# select * from person,hobby where person.hobby_id=hobby.id; # 原生sql
res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()
# join表,默认是inner join,自动按外键关联
# select * from Person inner Hobby on Person.hobby_id=Hobby.id; # 原生sql
res = session.query(Person).join(Hobby).all()
#isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可
# select * from Person left Hobby on Person.hobby_id=Hobby.id; # 原生sql
res = session.query(Person).join(Hobby, isouter=True).all()
# 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
res = session.query(Person).join(Hobby, Person.id == Hobby.id, isouter=True) # sql本身有问题,只是给你讲, 自己指定链接字段
# 右链接
res = session.query(Hobby).join(Person, isouter=True)
# 组合(了解)UNION 操作符用于合并两个或多个 SELECT 语句的结果集
# union和union all的区别?
q1 = session.query(User).filter(User.id > 40)
q2 = session.query(User).filter(User.id > 38)
res = q1.union(q2).all()
q1 = session.query(User.email).filter(User.id > 40)
q2 = session.query(User.email).filter(User.id > 38)
res = q1.union_all(q2).all()
print(len(res))
# 一对多,基于链表跨表查(__链表)
#方式一:直接连
res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id,Hobby.id>=2).all()
# 方式二:join连
res = session.query(Person).join(Hobby).filter(Person.id>=2).all()
# 多对多关系,基于链表的跨表查
#方式一:直接连
res = session.query(Boy, Girl,Boy2Girl).filter(Boy.id == Boy2Girl.boy_id,Girl.id == Boy2Girl.girl_id).all()
# 方式二:join连
res = session.query(Boy).join(Boy2Girl).join(Girl).filter(Person.id>=2).all()
session.commit()
session.close()
2)原生Sql
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Boy, Girl, Boy2Girl, Hobby, Book, User, Person
from sqlalchemy.orm import scoped_session
from sqlalchemy.sql import text
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 查询
cursor = session.execute('select * from users')
result = cursor.fetchall()
# 添加
# cursor = session.execute('insert into users(name) values(:value)',params={"value":'Like'})
# session.commit()
# print(cursor.lastrowid)
# print(result)
session.commit()
session.close()
五、SQL表模型之一对多
1)表模型
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='篮球')
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=True)
# hobby指的是tablename而不是类名
# 关联字段写在多的一方,写在Person中,跟hobby表中id字段做外键关联
hobby_id = Column(Integer, ForeignKey("hobby.id"))
# 跟数据库无关,不会新增字段,只用于快速链表操作
# 基于对象的跨表查询:就要加这个字段,取对象 person.hobby pserson.hobby_id
# 类名,backref用于反向查询
hobby = relationship('Hobby', backref='pers') # 如果有hobby对象,拿到所有人 hobby.pers
def __repr__(self):
return self.name
2)新增和基于对象的查询
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Person, Hobby
from sqlalchemy.orm import scoped_session
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 1 一对多关系新增
# hobby=Hobby(caption='足球')
# session.add(hobby) # 把hobby存入
# print(hobby.id)
# 两种方案:
# 方案一
# person=Person(name='lqz',hobby_id=1)
# session.add(person)
# 方案二:
# person=Person(name='张三',hobby=Hobby(caption='乒乓球'))
# person=Person(name='张三',hobby=Hobby(caption='乒乓球'))
# session.add(person)
## 方案三:
# hobby = session.query(Hobby).filter_by(id=2).first()
# print(hobby)
# person = Person(name='张刘', hobby=hobby)
# person = Person(name='张五', hobby_id=hobby.id)
# session.add(person)
# 2 基于对象的跨表查询
# 基于对象的跨表查询的正向查询
# person = session.query(Person).filter_by(id=1).first()
# print(person.hobby_id)
# print(person.hobby.caption)
# 基于对象的跨表查的反向
# hobby = session.query(Hobby).filter_by(id=2).first()
# # 喜欢这个爱好的所有人
# print(hobby.pers) # 列表
# for p in hobby.pers:
# print(p.name)
# 3 基于链表的跨表查 (先不急)
session.commit()
session.close()
六、SQL表模型之多对多
1)表模型
class Boy2Girl(Base):
__tablename__ = 'boy2girl'
id = Column(Integer, primary_key=True, autoincrement=True)
girl_id = Column(Integer, ForeignKey('girl.id'))
boy_id = Column(Integer, ForeignKey('boy.id'))
class Girl(Base):
__tablename__ = 'girl'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
def __str__(self):
return self.name
def __repr__(self):
return self.name
class Boy(Base):
__tablename__ = 'boy'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True, nullable=False)
# 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以
# 方便快速查询,写了这个字段,相当于django 的manytomany,快速使用基于对象的跨表查询
girls = relationship('Girl', secondary='boy2girl', backref='boys')
def __str__(self):
return self.name
def __repr__(self):
return self.name
2)增加和基于对象的跨表查询
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Boy, Girl, Boy2Girl
from sqlalchemy.orm import scoped_session
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 1 多对多新增
# 手动操作第三张表的方式
# boy = Boy(name='彭于晏')
# girl = Girl(name='刘亦菲')
# session.add(boy)
# session.add(girl)
# session.add_all([boy, girl])
# 建立关系:手动操作第三张表
# b=Boy2Girl(girl_id=1,boy_id=1)
# session.add(b)
##通过关联关系
# session.add(Boy(name='李清照', girls=[Girl(name='小红'), Girl(name='小黄')]))
# 拆开
# girl1 = Girl(name='小红1')
# girl2 = Girl(name='小黄1')
# boy = Boy(name='李清照1', girls=[girl2, girl1])
# session.add(boy)
# 基于对象的跨表查
# 正向
# boy = session.query(Boy).filter_by(id=2).first()
# print(boy)
# # 跟这个男孩越过的所有女生
# print(boy.girls)
# 通过girl查boy
# 反向
girl = session.query(Girl).filter_by(id=2).first()
print(girl)
print(girl.boys)
session.commit()
session.close()
七、Flask-SQLAlchemy的使用
# flask中使用sqlalchemy,直接使用
# 使用flask-sqlalchemy集成
1 导入 from flask_sqlalchemy import SQLAlchemy
2 实例化得到对象
db = SQLAlchemy()
3 将db注册到app中
db.init_app(app)
4 视图函数中使用session
全局的db.session # 线程安全的
5 models.py 中继承Base
db.Base
6 写字段
username = db.Column(db.String(80), unique=True, nullable=False)
八、Flask-Migrate的使用
# python manage.py makemigrations # 记录变化
# python manage.py migrate #把变化同步到数据库
# 使用步骤:
1 导入
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
2 注册
manager = Manager(app)
# 使用flask_migrate的Migrate 包裹一下app和db(sqlalchemy对象)
Migrate(app, db)
3 给flask_script增加一个db命令
# 把命令增加到flask-script中去
manager.add_command('db', MigrateCommand)
4 出现3条命令
python manage.py db init # 只执行一次,做初始化操作,以后再也不执行了,多出一个migrations文件夹
python manage.py db migrate #等同于django 的makemigrations
python manage.py db upgrade #等同于django 的migrate
到了这里,关于Flask从入门到放弃五(SQLAlchemy介绍与快速使用、创建表插入数据、Scoped_session线程安全、基本增删查改、表模型、Flask-SQLAlchemy、Migrate的使用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!