flask_day06:sqlalchemy快速插入 scope_session线程安全 基本增删改查 一对多 连表查询

sqlalchemy快速插入数据

sqlalchemy是ORM框架,跟其他web框架没有必然联系,可以独立使用

支持原生SQL,不能创建数据库,不能修改字段(增加、删除)

使用ORM插入

# 使用orm插入
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Book
# 第一步:生成engine对象
engine = create_engine(
    "mysql+pymysql://root@127.0.0.1:3306/aaa",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 第二步:拿到一个Session类,传入engine
Session=sessionmaker(bind=engine)

# 第三步:拿到session对象,相当于连接对象(会话)
session=Session()

# 第四步,增加数据
book=Book(name='红楼梦',)
session.add(book)
session.commit()
# 第五步:关闭session对象

session.close()

scoped_session线程安全

基本使用

from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# 第一步:生成engine对象
engine = create_engine(
    "mysql+pymysql://root@127.0.0.1:3306/aaa",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 第二步:拿到一个Session类,传入engine
Session = sessionmaker(bind=engine)
# 线程不安全
# session = Session()

# 做成线程安全的:如何做的?
# 内部使用了local对象,取当前线程的session,如果当前线程有,就直接返回用,如果没有,创建一个,放到local中
# session 是  scoped_session 的对象
session = scoped_session(Session)

# 以后全局使用session即可,它线程安全

image-20230410204420946

因为全局都用一个session对象,会有并发安全问题,于是sqlalchemy就使用scoped_session解决了这个问题

加在类上的装饰器

# session 是  scoped_session 的对象,类上没有属性和方法,但是,用的时候,确实用
session = scoped_session(Session) 


def speak():
    print('说话了')


def wrapper(func):
    def inner(*args, **kwargs):
        res = func()
        res.name = 'lqz'
        res.speak = speak
        return res

    return inner


@wrapper  # 语法糖会把Person当参数传入到装饰器中   Person=wrapper(Person)
class Person:
    pass


p = Person()

print(p.name)
p.speak()

基本增删查改

增、删、该,查的话有基本查询和高级查询。

from models import User, Book
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@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
session = scoped_session(Session)

# 1  增加:add   add_all
 user = User(name='pyy', email='44@qq.com', extra='摄氏度法')
 user1 = User(name='yyy', email='4@qq.com', extra='大沙发斯蒂芬')
 book = Book(name='西游记')
 # session.add(user)
 session.add_all([user, user1, book])  # 多个对象可以是models中任意表模型的对象

 session.commit()
 session.close()


# 2 基本查  filter  filter_by     filer:写条件     filter_by:等于的值
# filter
# 2.1 session.query(User)   中写表模型,可以写多个表模型(连表操作)  select * from User;
# 2.2 filter 过滤条件,必须写表达式  ==    >=    <=    !=   select * from user where user.id=1
# 2.3 all:普通列表  first
 user = session.query(User).filter(User.name == 'lqz').first()
 user = session.query(User).filter(User.name != 'lqz').all()
 print(user)
 res = session.query(User).filter(User.id > 1).all()
 print(res)

# filter_by  直接写等式    不能写成 User.name = 'lqz'
 user = session.query(User).filter_by(name='lqz').first()
 user = session.query(User).filter_by(id=2).first()
 user = session.query(User).filter_by(id=2).first()
 print(user)

# 3 删除(查到才能删) filter或filter_by查询的结果  不要all或first出来, .delete()即可
 res = session.query(User).filter_by(id=2).delete()
 session.commit()  # 一定不要忘了
 print(res) # 影响的行数


# 4 修改(查到才能改)
# 方式一:update修改
 res = session.query(User).filter_by(id=3).update({"name" : "彭于晏"})
 print(res)
 session.commit()
# 方式二,使用对象修改
 res = session.query(User).filter_by(id=3).first()
 res = session.query(User).filter_by(name='zzz').first()
 res.name='来来来'
 print(res.id)
 session.add(res)  # add 如果有主键,就是修改,如果没有主键就是新增
 session.commit()

增加:

image-20230410214203835

查看:

image-20230410220029696

修改:

image-20230411155825124

修改:

image-20230410221540838

基本增删查改和高级查询

# 高级查询(单标表部分)
# 查询: filer:写条件     filter_by:等于的值
# 询所有  是list对象
 res = session.query(User).all()  # 是个普通列表
 print(type(res))  # <class 'list'>
 print(len(res))  # 3

# 查询某几个字段
 原生sql语句: select name as xx,email from user;
 res = session.query(User.name.label('xx'), User.email)  # label取别名的意思
 print(res)  # 打出原生sql SELECT users.name AS xx, users.email AS users_email FROM users
print(res.all())  # [('uu099', '99@qq.com'), ('彭于晏099', '5@qq.com'), ('彭于晏099', '66@qq.com')]
for item in res.all():
  print(item[0])  # for循环拿出第0个位置的值是name
  
# filter传的是表达式,filter_by传的是参数
 res = session.query(User).filter(User.name == "uu099").all()  # [uu099] 是个列表里面放的对象__repr__所以才被打印出来了 不点all()打印出来的是SQL原生语句
 res = session.query(User).filter(User.name != "lqz").all()
 res = session.query(User).filter(User.name != "uu099", User.email == '66@qq.com').all()  # 两个条件相当于django中使用Q
 res = session.query(User).filter_by(name='uu099').all()
 res = session.query(User).filter_by(name='uu099', email='99@qq.com').all()   # 两个过滤条件既要满足name是多少又要email是多少
 print(res)
 print(len(res))
取一个all了后是list,list没有first方法
 res = session.query(User).first()
 print(res)

# 自定义查询(了解)
# from_statement 写纯原生sql
res = session.query(User).from_statement(text("SELECT * FROM users where email=:email")).params(email='99@qq.com').all()
# 是book的对象,但是查的是User表---能查到但不要这样写
 print(res)

# 条件查询

# and条件连接
 res = session.query(User).filter(User.id > 1, User.name == 'uu099').all() # and条件
 print(res)

# between
 res = session.query(User).filter(User.id.between(1, 9), User.name == 'uu099').all()
 res = session.query(User).filter(User.id.between(1, 9)).all()
 print(res)

# in
 res = session.query(User).filter(User.id.in_([1,3,4])).all()
 res = session.query(User).filter(User.email.in_(['3@qq.com','r@qq.com'])).all()

# ~非,除。。外
 res = session.query(User).filter(~User.id.in_([1,3,4])).all()
 print(res)

# 二次筛选
 res = session.query(User).filter(~User.id.in_(session.query(User.id).filter_by(name='lqz099'))).all()
 print(res)

# and or条件
from sqlalchemy import 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(User.id < 3, User.name == 'lqz099').all()  #  等同于上面
 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()
 select user.id from user where  user.name not like e%;
 res = session.query(User.id).filter(~User.name.like('e%'))
 print(res)

# 分页
# 一页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()
# 第一个条件重复后,再按第二个条件升序排
 res = session.query(User).order_by(User.name.desc(), User.id.asc())

# 分组查询  5个聚合函数
from sqlalchemy.sql import func
 res = session.query(User).group_by(User.extra)  # 如果是严格模式,就报错
# 分组之后取最大id,id之和,最小id  和分组的字段
 res = session.query(
     User.extra,
     func.max(User.id),
     func.sum(User.id),
     func.min(User.id)).group_by(User.extra).all()
 for item in res:
     print(item[2])
 having
 select max(id),sum(id),min(id) from  user group by  user.extra   having id_max>2;
 res = session.query(
     func.max(User.id),
     func.sum(User.id),
    func.min(User.id)).group_by(User.extra).having(func.max(User.id) > 2)

### 关联关系,基于连表的跨表查询
from models1 import Person,Hobby
# 链表操作
 select * from person,hobby where person.hobby_id=hobby.id;
 res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()

# 自己连表查询
# join表,默认是inner join,自动按外键关联
 select * from Person inner join Hobby on Person.hobby_id=Hobby.id;
 res = session.query(Person).join(Hobby).all()

#isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可
 select * from Person left join Hobby on Person.hobby_id=Hobby.id;
 res = session.query(Person).join(Hobby, isouter=True).all()
# 没有right join,通过这个实现
 res = session.query(Hobby).join(Person, isouter=True).all()

# # 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
# select * from Person left join Hobby on Person.id=Hobby.id;
 res = session.query(Person).join(Hobby, Person.hobby_id == Hobby.id, isouter=True) #  sql本身有问题,只是给你讲, 自己指定链接字段
# 右链接
 print(res)

原生sql

### 方式一:
# 第一步:导入
from sqlalchemy import create_engine
# 第二步:生成引擎对象
engine = create_engine(
    "mysql+pymysql://root@127.0.0.1:3306/cnblogs",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第三步:使用引擎获取连接,操作数据库
conn = engine.raw_connection()
cursor=conn.cursor()
cursor.execute('select * from aritcle')
print(cursor.fetchall())



### 方式二:
from models import User, Book
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa")
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')

image-20230411154949991

一对多

一对一:本身是一个表,拆成两个表,做一对一的关联;本质也是一对多,只不过关联字段唯一

一对多:关联字段写在多的一方

多对多:需要建立中间表;本质也是一对多

本质就只有一种外键关系

表模型

# 一对多关系
from sqlalchemy import create_engine
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.orm import relationship

# 第二步:执行declarative_base,得到一个类
Base = declarative_base()


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


engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa", )

# 把表同步到数据库  (把被Base管理的所有表,都创建到数据库)
Base.metadata.create_all(engine)

# 把所有表删除
# Base.metadata.drop_all(engine)

image-20230411195154985

新增和基于对象的查询

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from models1 import Hobby, Person

engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
session = scoped_session(Session)

# 一对多新增

 hobby = Hobby(caption='乒乓球')
 session.add(hobby)
 person = Person(name='张三')
 session.add(person)

 hobby=session.query(Hobby).filter(Hobby.caption=='乒乓球').first()
 # person = Person(name='王五',hobby_id=hobby.id)
 person = Person(name='王五',hobby_id=1)
 session.add(person)


# 支持按对象的增加方式,必须加relationship 做关联
# 方式一
 hobby=session.query(Hobby).filter(Hobby.caption=='乒乓球').first()# person = Person(name='赵六',hobby=hobby)
# 方式二
 hobby = Hobby(caption='羽毛球')  # 这是新增的,在hobby表中暂时没有
 person = Person(name='赵六', hobby=hobby)
 session.add_all([person, hobby])
 session.commit()




## 基于对象的跨表查询  .
# 正向查询
 person=session.query(Person).filter(Person.name=='王五').first()
 # print(person.hobby_id)
 print(person.hobby)  # Hobby 的对象

# 反向查询
 hobby=session.query(Hobby).filter(Hobby.id==1).first()
 print(hobby.pers)  #  pers是在relationship中定义的

连表查询

关联关系,基于连表的跨表查询

### 关联关系,基于连表的跨表查询
from models1 import Person,Hobby
# 链表操作
 select * from person,hobby where person.hobby_id=hobby.id;
 res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()

# 自己连表查询
# join表,默认是inner join,自动按外键关联
 select * from Person inner join Hobby on Person.hobby_id=Hobby.id;
 res = session.query(Person).join(Hobby).all()

#isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可
 select * from Person left join Hobby on Person.hobby_id=Hobby.id;
 res = session.query(Person).join(Hobby, isouter=True).all()
# 没有right join,通过这个实现
 res = session.query(Hobby).join(Person, isouter=True).all()

 # 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
 select * from Person left join Hobby on Person.id=Hobby.id;
 res = session.query(Person).join(Hobby, Person.hobby_id == Hobby.id, isouter=True) #  sql本身有问题,只是给你讲, 自己指定链接字段
# 右链接
 print(res)



# 多对多关系连表
# 多对多关系,基于链表的跨表查
#方式一:直接连
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()

总结回顾

1.sqlalchemy创建表:Base = declarative_base()

	只能创建和删除
  不能创建数据库
  不能修改表

2.快速插入数据

借助于session对象(注意去区别flask和Django的session只是同名而已,没有一点关系)
from sqlalchemy.orm import sessionmaker
Session=sessionmaker(bind=engine)
session=Session() # 回话连接的意思
session.add(表模型的对象)
session.add_all([对象, 对象])
session.commit()
session.close()

3.类装饰器

装饰类的装饰器:加在类上的装饰器、类作为装饰器用

加在类上的装饰器

def outer(cls):
  def inner(*args,**kwargs):
    res = cls()
    res.name = 'wyf'
    return res
  return inner

@outer
class Index:
  pass
res = Index()
print(res.name)

类作为装饰器来用

# 类作为装饰器来用
class Wrapper():
  def __init__(self, func):
    self.func = func
  def __call__(self, *args, **kwargs):
    # 前面加代码
    print('在你前面加')
    res = self.func(*args. **kwargs)
    # 后面加代码
    print('在你后面加')
    return res
@Wrapper  # add=Wrapper(add) ---> 触发Wrapper的__init__ ---> 现在add是Wrapper类的对象
def add():
  print('add')

4.基本增删改查:单表

增:add	add_all
查:filter:表达式	filter_by:具体值
删:查出来.delate()
改:
	查出来.update({'name':'jjj'})
  查出来.update({User.name:'xxx'})
  对象.name = 'kk'
  add(对象)
5 高级查询
	in	between	like	排序	分页	原生sql	分组
  
6 一对多关系的建立
	Person:hobby_id,hobby
  Hobby
  新增,基于对象的新增
  基于对象的跨表正向反向
  
7 多对多
	GIrl
  Boy
  Boy2Girl
  新增
  基于对象的跨表查询
  
8 连表查询
	res = res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()
  session.query(Person).join(Hobby).all()

flask-sqlalchemy使用

集成到flask中,直接使用sqlalchemy

有个第三方flask-sqlalchemy,帮助咱们快速的集成到flask

使用flask-sqlalchemy集成

安装:pip install 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 中继承Model
db.Model
6.写字段
username = db.Colum(db.String(80), unique=True, nullable=False)
7.配置文件中加入
  SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root@127.0.0.1:3306/ddd?charset=utf8"
    SQLALCHEMY_POOL_SIZE = 5
    SQLALCHEMY_POOL_TIMEOUT = 30
    SQLALCHEMY_POOL_RECYCLE = -1
    # 追踪对象的修改并且发送信号
    SQLALCHEMY_TRACK_MODIFICATIONS = False

book表提前就创建好了的

image-20230411205733892

flask-migrate使用

表发生变化,都会有记录,自动同步到数据库中

原生的sqlalchemy,不支持修改表的

flask-migrate可以实现类似于django的
python manage.py makemigrations #记录
python manage.py migrate        #真正的同步到数据库

使用步骤:

版本:flask:2.2.2 flask-script:2.0.3

1.第一步:安装,依赖与flask-migrate==2.7.0
2.在app所在的文件中
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
manager = Manager(app)
Migrate(app, db)
manager.add_command('db', MigrateCommand) 

manager.run() # 以后使用python manage.py runserver 启动项目
3.以后第一次执行一下
python manage.py db init  # 生成一个migrations文件夹,里面以后不要动,记录迁移的编号
4.以后在models.py 写表,加字段,删字段,改参数
5.只需要执行
python manage.py db migrate  # 记录
python manage.py db upgrade  # 真正的同步进去

flask项目演示

1 创建数据库 movie
2 pycharm打开项目
3 在models中,注释,解开注释,右键执行,迁移表
4 在models中恢复成原来的
5 在命令行中python manage.py runserver运行项目
6 访问前台和后台
posted @ 2023-04-10 23:19  小福福  阅读(132)  评论(0编辑  收藏  举报