day06
内容概要
- sqlalchemy快速插入数据
- scoped_session线程安全
- 基本增删查改
- 一对多
- 多对多
- 连表查询
- sqlalchemy自己集成flask
- flask-sqlalchemy使用
- flask-migrate使用
SQLAlchemy快速插入数据
models
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Index, Text, ForeignKey, DateTime, UniqueConstraint
from sqlalchemy.orm import relationship
# 第二步:执行declarative_base,得到一个类
Base = declarative_base()
class Book(Base):
id = Column(Integer, primary_key=True)
name = Column(String(32), nullable=False)
__tablename__ = "books"
def __str__(self):
return self.name # 打印的时候触发
def __repr__(self):
return self.name # 打印的时候在容器里面触发
class User(Base):
id = Column(Integer, primary_key=True)
name = Column(String(32), nullable=False)
__tablename__ = "users"
def __str__(self):
return self.name # 打印的时候触发
def __repr__(self):
return self.name # 打印的时候在容器里面触发
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/test2")
# 把表同步到数据(把被Base股那里的所有表,都创建到数据库)
Base.metadata.create_all(engine)
# 把所有表删除
# Base.metadata.create_all(engine)
py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Book
# 第一步: 生成engin对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/test2",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=1 # 多久之后对线程池中线程进行一次连接的回收(重置)
)
# 第二步: 拿到一个Session类, 传入engine
Session = sessionmaker(bind=engine)
# 第三步: 拿到session对象,相当于连接对象(会话)
session = Session()
# 第四步,增加数据
from models import Book
book = Book(name="红楼梦")
session.add(book)
session.commit()
session.close()

scoped_session线程安全
from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
"""
pool_recycle = -1 连接回收时间 -1,永不回收(推荐设置3600即1h)
注意: MySQL连接的默认断开时间是 8小时
"""
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/test2",
max_overflow=0, # 超过连接池大小外最多创建的连接个数
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 连接回收的时间
)
Session = sessionmaker(bind=engine)
# 线程不安全
# session = Session()
# 因为如果使用全局的session 会有并发安全问题
# scoped_session采用 local的方式,
# 把session复制多份每个都使用自己的这就解决的并发安全问题
# 做成线程安全的:如何做的?
# 内部使用了local对象,取当前线程的session,如果当前线程有,就直接返回用,如果没有,创建一个,放到local中
# session 是 scoped_session 的对象
session = scoped_session(Session)
# 以后全局使用session即可,它线程安全
加在类上的装饰器
类装饰器
-
加在类上的装饰器
def speak(): print("说话了") def wrapper(func): def inner(*args, **kwargs): func.NAME = "huwu" # 这是类属性 res = func(*args, **kwargs) res.name = 'lqz' # 这是对对象的属性 res.speak = speak return res return inner @wrapper class A: pass aa = A() print(aa.name) aa.speak() -
类当一个装饰器
# 类当装饰器 class A: def __init__(self, func, *args, **kwargs): self.func = func def __call__(self, *args, **kwargs): print("执行前") res = self.func(*args, **kwargs) print("执行后") return res @A # 相当于 aa = A(aa) def aa(): print("我是aa") aa() # 现在的 aa是 A的对象 print(aa.func)
基本的增删查改
add或add_all
from models import Book, User
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from sqlalchemy.sql import text
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/test2",
pool_timeout=30,
pool_size=5,
pool_recycle=-1, # 重置连接的时间, -1永不重置
max_overflow=0, # 超过连接池大小最多创建的个数
)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 1 增加:add add_all
user = User(name="lqz")
user1 = User(name="jason")
book = Book(name="西游记")
session.add_all([user, user1, book])
session.commit()
session.close()
-
查 filter filter_by filter:写条件, filter_by:等于值
all:普通列表 first 单个对象
# 2. 查 filter filter_by filter:写条件, filter_by:等于值 # res = session.query(User) # query里面写表模型, 可以写多个表模型 # print(res) # SELECT users.id AS users_id, users.name AS users_name FROM users # res = session.query(User).filter(User.name == "lqz") # print(res) # SELECT users.id AS users_id, users.name AS users_name FROM users WHERE users.name = %(name_1)s # res = session.query(User).filter(User.name == "lqz").all() # print(res) # [lqz] 列表套对象 # print(res[0].name) # lqz # res = session.query(User).filter_by(name='lqz') # print(res) # SELECT users.id AS users_id, users.name AS users_name FROM users WHERE users.name = %(name_1)s res = session.query(User).filter_by(name='lqz').all() res1 = session.query(User).filter_by(name='lqz').first() print(res, type(res)) # [lqz] <class 'list'> print(res1, type(res1)) # lqz <class 'models.User'> -
删除(查到才能删除)
filter或filter_by查询结果 不要all或者first出来# 删除 deleter res = session.query(User).filter(User.name == "lqz").delete() session.commit() print(res) # 影响的行数 1 -
修改(查到才能修改)
方式一:update修改
# 修改 res = session.query(User).filter(User.name == "jason").update({"name": "彭于晏"}) session.commit() session.close() res = session.query(User).all() print(res) # [彭于晏]方式二:使用对象修改
# 方式二 res = session.query(User).filter(User.name == "彭于晏").first() res.name = "lqz" session.add(res) session.commit() session.close() print(session.query(User).all()) # [lqz]add 如果有主键就修改,没有主键就新增
高级查询
-
只查询某几个字段
# select addr as xx from users; # res = session.query(User.addr.label("xx"), User.name) # print(res) # SELECT users.addr AS xx, users.name AS users_name FROM users # print(res.all()) # [('三大', 'jason'), ('撒大大撒', 'lqz')] # print(res.all()[0].name) # jason -
查询所有,使用占位符
# 查询所有,使用占位符 # select * from user where id<2 or name=lqz; res = session.query(User).filter(text("id<:value or name=:name")).params(value=2, name="lqz") # print(res) # SELECT users.id AS users_id, users.name AS users_name, users.addr AS users_addr FROM users WHERE id<%(value)s or name=%(name)s print(res.all()) # [jason, lqz] -
自定查询
# 自定义查询 # res = session.query(User).from_statement(text("select * from users where name=:name")).params(name="lqz").all() # print(res, type(res)) # [lqz] <class 'list'> res = session.query(User).from_statement(text("select * from books")).all() print(res, type(res)) print(res[0], type(res[0])) print(res[0].addr) # 用的还是原来, 不要这么写 -
and连接# res = session.query(User).filter(User.id>1, User.name=='lqz').all() # print(res) # [lqz] -
in# in_ res = session.query(User).filter(User.id.in_([1, 3])).all() print(res) -
between什么和什么之间# between res = session.query(User).filter(User.id.between(1, 3)).all() print(res) -
~非,除...外# 非 res = session.query(User).filter(~User.id.in_([1, 3])).all() print(res) # [lqz] -
二次筛选
# 二次筛选 res = session.query(User).filter(~User.id.in_(session.query(User.id).filter(User.name == 'lqz'))).all() print(res) # [jason] -
and,or条件from sqlalchemy import and_, or_ # and or 条件 # or_包裹的都是or条件, and_包裹的都是and条件 # res = session.query(User).filter(and_(User.id) >=3, User.name == "lqz").all() # print(res) # [] res = session.query(User).filter(or_(User.id >=3, User.name == "lqz")).all() print(res) # [lqz] # 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.name.like("%q%")).all() print(res) # [lqz] -
分页
# 分页 # 一页2条,查第五页 res = session.query(User)[2 * 5:2 * 5 + 2] -
排序
# 排序 res = session.query(User).order_by(User.id.desc()).all() print(res) # [lqz, jason] -
分组
# 分组查询 5个 聚合函数
from sqlalchemy.sql import func
# res = session.query(User).group_by(User.extra) # 如果是严格模式,就报错
from sqlalchemy.sql import func
# 分组之后取最大id, id之和, 最小id 和分组的字段
# res = session.query(User.addr, func.max(User.id), func.sum(User.id), func.min(User.id)).group_by(User.addr).all()
#
# print(res) # [('三大', 3, Decimal('4'), 1), ('撒大大撒', 4, Decimal('6'), 2)]
res = session.query(func.max(User.id), func.sum(User.id)).group_by(User.addr).having(func.max(User.id) > 3).all()
print(res) # [(4, Decimal('6'))]
原生sql
方式一:
# from sqlalchemy import create_engine
# engine = create_engine(
# "mysql+pymysql://root:123@127.0.0.1:3306/test2",
# max_overflow=0, # 超过连接池大小外最多创建的连接
# pool_size=5, # 连接池大小
# pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
# pool_recycle=-1, # 多久之后对线程池中的线程进行一次连接的回收
# )
# conn = engine.raw_connection()
# cursor = conn.cursor()
# cursor.execute("select * from users")
# print(cursor.fetchall())
方式二:
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/test2"
)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
"""
2.0.9 版本需要使用text包裹一下,原来版本不需要
cursor = session.execute(text('select * from users'))
result = cursor.fetchall()
print(result)
"""
cursor = session.execute(text("insert into books(name) values(:name)"), params={"name": "三国演义"})
session.commit()
print(cursor.lastrowid)
session.close()
django执行原生sql
# 选择的查询基表Book.objects.raw ,只是一个傀儡,正常查询出哪些字段,都能打印出来
def index(request):
# books = Book.objects.raw('select * from app01_book where id=1') # RawQuerySet 用起来跟列表一样
# books = Publish.objects.raw('select * from app01_book where id=1') # RawQuerySet 用起来跟列表一样
# print(books[0])
# print(type(books[0]))
# # for book in books:
# # print(book.name)
# # print(books[0].name)
# print(books[0].addr) #也能拿出来,但是是不合理的
res = Book.objects.raw('select * from app01_publish where id=1') # RawQuerySet 用起来跟列表一样
print(res[0])
print(type(res[0]))
print(res[0].name)
# book 没有addr,但是也打印出来了
print(res[0].addr)
return HttpResponse('ok')
一对多
一对一:本身是一个表,拆成两个表,做一对一的关联 本质就是一对多,只不过关联字段唯一
一对多:管关联字段写在多的一方
多对多:需要建立中间表,本质也是一对多
本质就只有一种外键关系
models
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.orm import relationship
Base = declarative_base()
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
name = Column(String(32))
# publish指的是 tablename名不是类名
# 关联字段写在多的一方,写在Book中 跟 publish_id做外键关联
publish_id = Column(Integer, ForeignKey("publish.id"))
publish = relationship("Publish", backref='books')
# 跟数据库无关, 不会新增字段,只用于快速连表操作
# 基于对象的跨表查询:就要加这个字段,取对象book.publish
# book.publish_id
# 类名 backref用于反向查询
def __repr__(self):
return self.name
class Publish(Base):
__tablename__ = 'publish'
id = Column(Integer, primary_key=True)
name = Column(String(32))
addr = Column(String(64))
def __repr__(self):
return self.name
# engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/test3")
# Base.metadata.create_all(engine)
#
新增
方式一
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from models1 import Book, Publish
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/test3")
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 一对多新增
# book = Book(name="三国演义")
# session.add(book)
# publish = Publish(name="北京出版社", addr="北京")
# session.add(publish)
#
# session.commit()
# session.close()
publish = session.query(Publish).filter(Publish.name=="北京出版社").first()
book = session.query(Book).filter(Book.name=='三国演义').first()
book.publish_id = publish.id
session.add(book)
session.commit()
session.close()
方式二
publish = Publish(name="河南出版社", addr="河南")
book = Book(name="水浒传", publish=publish)
session.add_all([publish, book])
session.commit()
正反向查询
# 基于对象的跨表查询
# 反向查询
publish = session.query(Publish).filter(Publish.name == "河南出版社").first()
print(publish.books)
# 正向查询
book = session.query(Book).filter(Book.name=="水浒传").first()
print(book.publish)
多对多
models
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.orm import relationship
Base = declarative_base()
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
name = Column(String(32))
# publish指的是 tablename名不是类名
# 关联字段写在多的一方,写在Book中 跟 publish_id做外键关联
publish_id = Column(Integer, ForeignKey("publish.id"))
publish = relationship("Publish", backref='books')
# 跟数据库无关, 不会新增字段,只用于快速连表操作
# 基于对象的跨表查询:就要加这个字段,取对象book.publish
# book.publish_id
# 类名 backref用于反向查询
author = relationship("Author", secondary="author2book", backref='books')
def __str__(self):
return self.name
def __repr__(self):
return self.name
class Publish(Base):
__tablename__ = 'publish'
id = Column(Integer, primary_key=True)
name = Column(String(32))
addr = Column(String(64))
def __str__(self):
return self.name
def __repr__(self):
return self.name
class Author2Book(Base):
__tablename__ = "author2book"
id = Column(Integer, primary_key=True)
book_id = Column(Integer, ForeignKey("books.id"))
publish_id = Column(Integer, ForeignKey("publish.id"))
class Author(Base):
__tablename__ = "author"
id = Column(Integer, primary_key=True)
name = Column(String(32))
def __str__(self):
return self.name
def __repr__(self):
return self.name
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/test3")
Base.metadata.create_all(engine)
# Base.metadata.drop_all(engine)
py
方式一
#
from models1 import Author, Author2Book
book = Book(name="红楼梦")
author = Author(name="jason")
session.add_all([book, author]) # 先执行 被关联表
# session.add(Author2Book(book_id=1, author_id=1)) # 然后在执行关联表
session.commit()
# session.close()
方式二
# 方式二
book = Book(name="西游记")
author = Author(name="jason")
book.author = [author, ] # 因为是多 所以用列表包裹
session.add(book)
session.commit()
正反向查询
# 跨表查询
# 正向
book = session.query(Book).filter(Book.name == "西游记").first()
print(book.author)
# 反向
author = session.query(Author).filter(Author.name == "jason").first()
print(author.books)
连表操作
一对多
关联关系,基于连表的跨表查询
# 连表操作
# select * from books, publish where books.publish_id = publish.id
res = session.query(Book, Publish).filter(Book.publish_id == Publish.id).all()
print(res)
自己连表查询
# 自己连表操作 join表, 默认是 inner join 自动外键关联
# res = session.query(Book).join(Publish).all()
# print(res)
# print(res[0].name)
# print(res[0].publish.name)
# isouter=True 外连, 表示 book left join publish 没有右连接
# select * from books left join publish on books.publish_id = publish.id
# res = session.query(Book).join(Publish, isouter=True)
# print(res)
# print(res.all())
# 自己指定on条件(连表条件),第二个参数,支持on多个条件用and_,同时
# select * from books left join publish on books.publish_id = publish.id
res = session.query(Book).join(Publish, Book.publish_id == Publish.id, isouter=True)
print(res)
print(res.all())
多对多
# 多对多
# 方式一:直连
# res = session.query(Book, Author2Book, Author).filter(Book.id == Author2Book.book_id, Author.id == Author2Book.author_id).all()
# print(res)
# 方式二 join连接
res = session.query(Book).join(Author2Book).join(Author).filter(Book.id > 1).all()
print(res)
sqlalchemy自己集成flask
集成到flask中,直接使用sqlalchemy
models
###使用原生sqlalchemy
# 第一步:导入
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
Base = declarative_base()
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
name = Column(String(32))
if __name__ == '__main__':
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/test1")
# 把表同步到数据库 (把被Base管理的所有表,都创建到数据库)
Base.metadata.create_all(engine)
py
from flask import Flask
from sqlalchemy import create_engine
from sqlalchemy.orm import session, sessionmaker
from sqlalchemy.orm import scoped_session
engin = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/test1",
pool_recycle=-1,
pool_timeout=30,
pool_size=5,
max_overflow=0,
)
Session = sessionmaker(engin)
session_db = scoped_session(Session)
app = Flask(__name__)
@app.route("/")
def home():
from models import Book
session_db.add(Book(name="西游记"))
session_db.commit()
session_db.close()
return "增加"
if __name__ == '__main__':
app.run()
flask-sqlalchemy使用
下载pip3 install flask-sqlalchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123@127.0.0.1:3306/test1'
db = SQLAlchemy()
db.init_app(app)
# class Publish(db.Model):
# id = db.Column(db.Integer, primary_key=True)
# name = db.Column(db.String(32))
# class Book(db.Model):
# __tablename__ = 'books'
# id = db.Column(db.Integer, primary_key=True)
# name = db.Column(db.String(32))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
# db.Column(db.Integer)
if __name__ == '__main__':
with app.app_context():
db.create_all()
"""
从FlASK-SQLAlChemy 3.0开始,对db.engine(和db.session)的所有访问都需要活动的FlaskTM应用程序上下文.db.create_all使用db.engine,因此它需要应用程序上下文.
复制代码
with app.app_context():
db.create_all()
"""
flask-migrate使用
表发生变化,都会右记录,自动同步到数据库中
原生的sqlalchemy,不支持修改表的
flask-migrate可以实行类似与django的
python38 manage.py db init 只执行一次,初始化的时候时候使用
python38 manage.py db migrate 写入迁移文件
python38 manage.py db upgrade 同步数据库
from apps import create_app
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from apps import db
from apps.models import User
app = create_app()
# flask-script的使用
# 第一步,初始化出flask_script的 manage
manager = Manager(app)
Migrate(app, db)
# 第二步 使用flask_migrate的Migrate 包裹一下app 和db(sqlalchemy对象)
manager.add_command("db", MigrateCommand)
if __name__ == '__main__':
manager.run()
# app.run()
"""
python38 manage.py db init 只执行一次,初始化的时候时候使用
python38 manage.py db migrate 写入迁移文件
python38 manage.py db upgrade 同步数据库
"""





浙公网安备 33010602011771号