Flask入门四(SQLAlchemy快速使用、原生sql操作、操作表、表模型、基于Scoped_Session实现线程安全、基本增删查改、高级查询、Flask-SQLAlchemy的使用)

发布于:2024-07-08 ⋅ 阅读:(44) ⋅ 点赞:(0)

一、SQLAlchemy介绍和快速使用

1.介绍

SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

SQLAlchemy是一个企业级的orm框架,Django的orm框架,只能在Django框架中使用,不能在别的地方使用,而SQLAlchemy可以单独使用,也可以用在其他框架中

	-flask : sqlalchemy
    -fastapi:sqlalchemy
    
	python界的orm框架
		-django orm
		-sqlalchemy
		-peewee

组成部分:

  • Engine,框架的引擎
  • Connection Pooling ,数据库连接池
  • Dialect,选择连接数据库的DB API种类
  • Schema/Types,架构和类型
  • SQL Exprression Language,SQL表达式语言

安装

	pip install  sqlalchemy

SQLAlchemy本身无法操作数据库,其必须依赖pymsql等第三方插件,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...]
	    
	更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html

	# django中如何反向生成models
	python manage.py inspectdb > app/models.py

2.sqlalchemy原生操作

	'使用sqlalchemy操作原生sql'
	import pymysql
	
	1.导入
	from sqlalchemy import create_engine
	from sqlalchemy.engine.base import Engine
	
	2.创建引擎
	engine = create_engine(
	    "mysql+pymysql://root:1234@127.0.0.1:3306/cnblogs",
	    max_overflow=0,  # 超过连接池大小外最多创建的连接
	    pool_size=5,     # 连接池大小
	    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
	    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
	)
	
	'================================'
	
	3.使用引擎拿到链接
	conn = engine.raw_connection()
	4.剩下的和之前操作sql一样,使用pymysql
	cursor=conn.cursor(pymysql.cursors.DictCursor)
	cursor.execute('select * from article limit 10')
	res = cursor.fetchall()
	print(res)
	
	
	'使用多线程测试:'
	from threading import Thread
	def task(arg):
	    conn = engine.raw_connection()
	    cursor = conn.cursor()
	    cursor.execute('select * from article')
	    res = cursor.fetchall()
	    print(res)
	    cursor.close()
	    conn.close()
	
	
	for i in range(20):
	    t = Thread(target=task,args=(i,))
	    t.start()

二、sqlalchemy操作表

1.创建、删除表

1.导入
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
Base = declarative_base()  # Base当成models.Model

2.创建表模型
class User(Base):
    __tablename__ = 'users'  # 创建的表名
    # 写字段
    id = Column(Integer,primary_key=True,autoincrement=True)  # 主键、自增
    name = Column(String(32), index=True, nullable=False)  # name列,索引,不可为空  普通索引
    email = Column(String(32), unique=True)  # 唯一索引
    # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
    ctime = Column(DateTime, default=datetime.datetime.now)
    extra = Column(Text)  # 大文本

3.没有命令,后期可以使用第三方模块,可以有命令操作
# 目前需要手动做
# sqlalchemy 不能创建数据库,能创建表,不能删除增加字段(第三方模块)
3.1.创建引擎
engine = create_engine(  # 需要自己手动创建库
    "mysql+pymysql://root:1234@127.0.0.1:3306/sqlalchemy01",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,     # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
3.2.把表模型同步到数据库中(能创建删除,不能修改)
Base.metadata.create_all(engine)

3.3.删除表
Base.metadata.drop_all(engine)

2.简单操作(orm)

from models import User

1.创建引擎
from sqlalchemy import create_engine
engine = create_engine(  # 需要自己手动创建库
    "mysql+pymysql://root:1234@127.0.0.1:3306/sqlalchemy01",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

2.orm操作,借助于engine 得到session(conn)对象
from sqlalchemy.orm import sessionmaker
Connection = sessionmaker(bind=engine)
conn = Connection()  # 不是flask中的session,而是会话,conn的

3. 拿到(conn)会话对象后,进行orm操作
3.1 增加数据
user = User(name='jack',email='1@qq.com')
# 插入到数据库
conn.add(user)  # 放个对象
# 提交
conn.commit()
# 关闭连接
conn.close()

3.2 查询数据
# 查询User表中id为1的所有数据-->是一个列表
res = conn.query(User).filter_by(id=1).all()
print(res)


3.3 删除数据
res = conn.query(User).filter_by(name='jack').delete()
print(res) # 返回删除的条数
conn.commit() # 提交

3.4 修改
res = conn.query(User).filter_by(name='jack').update({'name':'oscar','extra':'xxxx'})
conn.commit()

三、SQL表模型:一对多关系

1.表模型

import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index

Base = declarative_base()  # Base 当成 models.Model

# 一对多 :一个兴趣被多个人喜欢  一个人只喜欢一个兴趣
class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='篮球')

    def __str__(self):
        return self.caption

    def __repr__(self):
        return self.caption



class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=True)
    # hobby指的是tablename而不是类名,uselist=False
    # 外键关联--》强外键--》物理外键
    hobby_id = Column(Integer, ForeignKey("hobby.id")) # 加一个unique=True就是一对一的外键关系

    # 跟数据库无关,不会新增字段,只用于快速连表操作
    # 基于对象的跨表查询:就要加这个字段,取对象  person.hobby     pserson.hobby_id
    # 类名,backref用于反向查询
    hobby = relationship('Hobby', backref='pers')

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


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

    # 把表模型同步到数据库中
    Base.metadata.create_all(engine)

2.新增和基于对象的跨表查询

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Hobby, Person

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

Session = sessionmaker(bind=engine)
session=Session()

'''一对多关系新增'''
# 增加 Hobby
hobby = Hobby(caption='足球')
hobby1 = Hobby()
session.add_all([hobby,hobby1])
session.commit()

'''三种新增方案'''
# 方案一
person = Person(name='jack',hobby_id=1)
person1 = Person(name='tom',hobby_id=2)
session.add_all([person,person1])
session.commit()

# 方案二
# 增加person表数据时直接增加关联的hobby表数据
hobby = Hobby(caption='乒乓球')
person = Person(name='oscar',hobby=hobby) # 这里的直接使用对象的方式,前提是表模型中必须有relationship
session.add(person)
person1 = Person(name='kami',hobby=Hobby(caption='棒球'))
session.add(person1)
session.commit()


# 方案三
hobby = session.query(Hobby).filter_by(id=1).first()
print(hobby)
person = Person(name='ankn',hobby=hobby)
person1 = Person(name='tank',hobby_id=hobby.id)
session.add_all([person,person1])
session.commit()


'''基于对象的跨表查询--正向'''
person = session.query(Person).filter_by(id=2).first()
# person = session.query(Person).filter_by(name='tom').first()
print(person)  # tom
# 基于对象的跨表查询,正向查询
print(person.hobby.caption)  # 篮球
print(person.hobby_id)  # 2


'''基于对象的跨表查询--反向'''
hobby = session.query(Hobby).filter_by(id=1).first()
# hobby = session.query(Hobby).filter_by(caption='足球').first()
print(hobby)  # 足球
# 基于对象的跨表查询,反向查询
print(hobby.pers)  # [jack, ankn, tank] 是一个列表套对象
print(hobby.pers[1].name)
# for i in hobby.pers:  # 循环拿出每一个对象
#     print(i.name)

四、SQL表模型:多对多关系

1.表模型

import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index

Base = declarative_base()  # Base 当成 models.Model

'''多对多'''
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'))
    # boy = relationship('Boy', backref='boy')


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)

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

    # 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以--等同于manytomany
    # 方便快速查询,写了这个字段,相当于django 的manytomany,快速使用基于对象的跨表查询
    girls = relationship('Girl', secondary='boy2girl', backref='boys')



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

    # 把表模型同步到数据库中
    Base.metadata.create_all(engine)

2.新增和基于对象的跨表查询

	from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Girl, Boy, Boy2Girl

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

Session = sessionmaker(bind=engine)
session=Session()


'''多对多关系新增'''
1.手动操作第三张表,建立关系
# 增加Boy
boy = Boy(name='彭于晏')
boy1 = Boy(name='吴彦祖')
boy2 = Boy(name='金城武')
session.add(boy)
session.add_all([boy1, boy2])
# 增加Girl
girl = Girl(name='金玟庭')
girl1 = Girl(name='田振姬')
girl2 = Girl(name='赵美延')
session.add(girl)
session.add_all([girl1, girl2])
session.commit()
# 建立外键关系:手动操作第三张表
obj = Boy2Girl(boy_id=3, girl_id=1)
obj1 = Boy2Girl(boy_id=3, girl_id=2)
obj2 = Boy2Girl(boy_id=3, girl_id=3)
session.add_all([obj, obj1, obj2])
session.commit()

2.通过关联关系
girl = Girl(name='沈小婷')
girl1 = Girl(name='柳智敏')
boy = Boy(name='古天乐',girls=[girl,girl1])  # 可以多条
session.add(boy)
session.commit()


'''基于对象的跨表查询--正向'''
boy = session.query(Boy).filter_by(name='古天乐').first()
print(boy)
# 基于对象的跨表查询,正向查询
print(boy.girls)  # 列表套对象
print(boy.girls[0])
for i in boy.girls:  循环取出对象
    print(i.name)


'''基于对象的跨表查询--反向'''
girl = session.query(Girl).filter_by(name='赵美延').first()
print(girl)
# 基于对象的跨表查询,反向查询
print(girl.boys)  # 列表套对象

五、基于Scoped_Session实现线程安全

session对象,集成到flask中去,要把session对象做成全局(大家公用),还是每个视图函数独有一个(每次都要实例化得到这个session对象)不是session_key,而是链接会话session

每个视图函数独有一个,每次都要实例化,sqlalchemy提供了一种方式,让咱们使用全局的一个session,但是每个视图函数中使用的都是不同的request,session都是这种实现机制

sqlalchemy提供了一种,在不同线程中,虽然使用全局session,实际上每个线程自己独有一个session

1.线程安全

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:1234@127.0.0.1:3306/sqlalchemy02", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)

"""
session是链接对象,如果集成到flask中,我们是把session定义成全局还是每个视图函数一个session呢?
正常来讲要每个视图函数定义一个session,有些麻烦。
sqlalchemy 帮咱提供了一个只要定义一次的session,能够做到在不同线程中,使用的是自己的session,底层基于local。
"""
1.原来获得 session对象
# session=Session()  # 放在flask中,这样如果做成全局,线程就不安全,想要安全就得在视图函数中每次都实例化得到
# print(type(session))
from sqlalchemy.orm import scoped_session
from threading import local
from models import User
2.使用scoped_session得到session对象,保证线程安全,本意就是每个线程使用自己独立的session
session=scoped_session(Session)
print(type(session))
############# 执行ORM操作 #############
obj = User(name='jack',email='1@qq.com')
session.add(obj)
# 提交事务
session.commit()
# # 关闭session
session.close()

2.研究

'''
研究1:session对象,现在不是原来的session对象了
session=Session()  <class 'sqlalchemy.orm.session.Session'>
session=scoped_session(Session)  <class 'sqlalchemy.orm.scoping.scoped_session'>
'''

'''
研究2:类上加装饰器,能够给对象,增加属性或方法
'''
# 加个showName方法和name属性
def showName():
    print('xxxxx')


def add(func):
    def inner(*args,**kwargs):
        res = func(*args,**kwargs) # Person()
        res.name = 'jack'
        res.showName=showName
        return res
    return inner

@add  # Person=add(Person)
class Person:
    pass

p = Person()
print(p.name)  # jack
p.showName()

'''
研究3:如何保证线程安全
    补充from threading import local,local是一个类,类加括号实例对象
     l=local() # 而local对象线程是安全的,不需要加锁
     这个l,可以被多个线程并发操作, 而且不会数据错乱
     如何实现的?内部通过线程id号作区分,取值赋值用的都是自己的那部分,例如
         线程1:l.name='jack'
         线程2:l.name='tom'
        # 线程1上取name,永远取出来的是线程1当时放入的值,线程二也是一样
        
        
    session:scoped_session独享
    session.add------》return self._proxied.add()
    self._proxied----》return self.registry()
    self.registry = ThreadLocalRegistry(session_factory)
    ThreadLocalRegistry(session_factory)--》这个对象加括号,会触发它的__call__
        def __call__(self) -> _T:
            try:
                # 这个register就是self.registry = threading.local(),所以它是线程安全的
                # 所以不同线程都是使用自己的value,也就是session对象
                return self.registry.value  # 不同线程有自己的一个session对象
            except AttributeError:
                val = self.registry.value = self.createfunc()  # 所以这个就是Session()
                return val
'''

六、基本增删查改和高级查询

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from models import Boy, Girl, Boy2Girl, Hobby, User, Person
from sqlalchemy.sql import text

engine = create_engine("mysql+pymysql://root:1234@127.0.0.1:3306/sqlalchemy02", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = scoped_session(Session)  # 线程安全

'''1.基本增加:add、add_all'''
# add_all
hobby = Hobby(caption='羽毛球')
hobby1 = Hobby(caption='橄榄球')
session.add_all([hobby,hobby1])
# add
person = Person(name='彭于晏',hobby=hobby)
session.add(person)
session.commit()

'''2.删除'''
# 方式一
session.query(User).filter_by(id=1).delete()
# 方式二: session.delete(对象)
user = session.query(User).filter_by(id=1).first()
session.delete(user)


'''3.修改,更新'''
# 方式一
session.query(User).filter_by(id=1).update({'name':'oscar'})
session.commit()
# 方式二:类名.属性名,作为要修改的key
session.query(Boy).filter_by(id=4).update({Boy.name:'xxxx'})
session.commit()
# 方式三:
# 对象.name='xxx' session.add(对象)
boy=session.query(Boy).filter_by(id=1).first()
boy.name='xxl'
session.add(boy) # 有id就是修改,没有就是新增
session.commit()


# 类似于django的F查询
# 当字符串拼接
session.query(User).filter_by(id=1).update({'name':User.name+'_nb'},synchronize_session=False)
# 当字符串相加
session.query(User).filter_by(id=1).update({'id':User.id+6}, synchronize_session="evaluate")
session.commit()


'''4.查询--->基本查询'''
# 1.filter_by 写条件
res=session.query(User).filter_by(name='jack',id=1).first()
res=session.query(User).filter_by(name='jack').all()  # 放在列表中 不是queryset对象
print(res)

# 2.filter 写表达式
res=session.query(User).filter(User.name=='jack').first()
res=session.query(User).filter(User.id>=3).all()
res=session.query(User).filter(User.name!='xxxx').all()
print(res)

# 3.只查表中某几个字段,并重命名
# select name as xx,age from user;
res=session.query(User.name.label('xx'), User.email)

# select name,email from user;
res=session.query(User.name, User.email).all()
res=session.query(User)


# 4.条件可以使用text自己拼凑  占位符
# select * from user where id< 224 and name=jack order by id
res = session.query(User).filter(text("id<:value and name=:name")).params(value=224, name='jack').order_by(User.id).all()
print(res)


# 5.直接写原生sql
# SELECT * FROM users where name=jack
res = session.query(User).from_statement(text("SELECT * FROM users where name=:name")).params(name='jack').all()
res = session.query(User).from_statement(text("SELECT * FROM users where name=:name")).params(name='张三')
print(res)

print('=======================================')

'高级查询'
'''表达式,and条件连接'''
res = session.query(User).filter(User.id > 1, User.name == 'jack').all()  # and条件

'''between'''
# select * from User where user.id between 1 and 3 and name=jack
res = session.query(User).filter(User.id.between(1, 3), User.name == 'jack').all()

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


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


'''二次筛选'''
# select * from User where id not in (select id from User where name=jack)
res = session.query(User).filter(~User.id.in_(session.query(User.id).filter_by(name='jack'))).all()


'''and or条件'''
from sqlalchemy import and_, or_
# or_包裹的都是or条件,and_包裹的都是and条件
res = session.query(User).filter(and_(User.id >= 3, User.name == 'jack')).all()  # and条件
# 默认就是and条件,结果和上面一样
res = session.query(User).filter(User.id >= 3,User.name=='jack').all()

# select * from User where id < 2 or name = jack;
res = session.query(User).filter(or_(User.id < 2, User.name == 'jack')).all()  # or条件
res = session.query(User).filter(
    or_(
        User.id < 2,
        and_(User.name == 'jack', User.id > 3),
        User.extra != ""
    )).all()


'''通配符,模糊查询'''
# select * from User where email like %@%;
res = session.query(User).filter(User.email.like('%@%')).all()
res = session.query(User.id,User.name).filter(~User.name.like('j%')).all()  # 不以什么开头


'''限制,用于分页,区间'''
res = session.query(User)[2 * 5:2 * 5 + 2] # 切片可以做运算
res = session.query(User)[2:4] # 从零开始,顾头不顾尾


'''排序,根据email降序排列(从大到小)'''
res = session.query(User).order_by(User.email.desc()).all()
res = session.query(User).order_by(User.email.asc()).all()
# 第一个条件重复后,再按第二个条件升序排
res = session.query(User).order_by(User.name.desc(), User.id.asc()).all()


'''分组查询'''
from sqlalchemy.sql import func
# select name from User group by name; 一旦分组后,就只能查询 分组后的字段和聚合函数的字段
res = session.query(User.name).group_by(User.name).all()
# 分组之后取最大id,id之和,最小id
res = session.query(
    func.max(User.id),  # 最大
    func.sum(User.id),  # 总和
    func.min(User.id),  # 最小
    func.count(User.id),  # 数量
    User.name).group_by(User.name).all()
for item in res:  # 循环拿出列表里面的每一个元组
    print(item)


'''having筛选'''
# select max(id),sum(id),min(id) from User group by name having max(id)>2;
res = session.query(
    func.max(User.id),
    func.sum(User.id),
    func.min(User.id)).group_by(User.name).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()
res = session.query(Hobby).join(Person, isouter=True).all()  # 反过来顺序就是右链接
# 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
res = session.query(Person).join(Hobby, Person.id == Hobby.id, isouter=True)  # sql本身有问题,只是给你讲, 自己指定链接字段


'''union和union all'''
# 组合(了解)UNION 操作符用于合并两个或多个 SELECT 语句的结果集
q1 = session.query(Boy.id,Boy.name).filter(Boy.id > 1)
q2 = session.query(Girl.id,Girl.name).filter(Girl.id < 5)
# union 去重
res = q1.union(q2).all()
print(res)
print(len(res))

q1 = session.query(Boy.name).filter(Boy.id > 1)
q2 = session.query(Girl.name).filter(Girl.id < 5)
res = q1.union_all(q2).all()
print(res)
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()

print(res)
session.commit()
session.close()

七、Flask-SQLAlchemy的使用

1.flask中集成sqlalchemy使用(自己做)

	'创建一个flask项目'
	'''
		项目结构
		src
			-__init__.py
			-db.py
			-models.py
			-settings.py
			-views.py
		templates
			-index.html
		app.py
	'''

	'app.py'
	from src import app
	if __name__ == '__main__':
	    app.run()

	'settings.py'
	DEBUG=True

	'__init__.py'
	from flask import Flask
	app = Flask(__name__, template_folder='../templates')
	app.config.from_pyfile('./settings.py')
	
	# 注册蓝图
	from src.views import bp_user
	app.register_blueprint(bp_user)
	
	'db.py'
	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:1234@127.0.0.1:3306/sqlalchemy02", max_overflow=0, pool_size=5)
	Session = sessionmaker(bind=engine)
	session = scoped_session(Session)  # 线程安全

	'models.py'
	import datetime
	from sqlalchemy import create_engine
	from sqlalchemy.orm import declarative_base, relationship
	from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
	Base = declarative_base()  # Base 当成 models.Model
	# 单表
	class User(Base):
	    __tablename__ = 'users'  # 表名
	    # 写字段
	    id = Column(Integer, primary_key=True, autoincrement=True)  # id 主键
	    name = Column(String(32), index=True, nullable=False)  # name列,索引,不可为空
	    email = Column(String(32), unique=True)
	    # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
	    ctime = Column(DateTime, default=datetime.datetime.now)
	    extra = Column(Text)
	
	    def __str__(self):
	        return self.name
	
	    def __repr__(self):
	        return self.name
	
	if __name__ == '__main__':
	    # 创建引擎
	    engine = create_engine(
	        "mysql+pymysql://root:1234@127.0.0.1:3306/sqlalchemy02",
	        max_overflow=0,  # 超过连接池大小外最多创建的连接
	        pool_size=5,  # 连接池大小
	        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
	        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
	    )
	
	    # 把表模型同步到数据库中
	    Base.metadata.create_all(engine)

	'views.py'
	from flask import render_template,jsonify
	from flask import Blueprint
	bp_user = Blueprint('user',__name__)
	from .db import session
	from .models import User
	
	@bp_user.route('/')
	def index():
	    # 把用户表中所有数据都查出来,返回给前端
	    res = session.query(User).all()
	    '''前后端分离传入数据'''
	    # 不能直接放入jsonify中,json序列化,只能序列化 数字、字符串、列表、布尔和字典
	    # res是列表套对象,是不能直接序列化的
	    # l = []
	    # for i in res:
	    #     l.append({'name': i.name, 'email': i.email})
	    # return jsonify({'code': 100, 'msg': '成功', 'results': l})
	    '''前后端混合传入数据'''
	    return render_template('index.html',users=res)
	
	'index.html'
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>Title</title>
	</head>
	<body>
	<h1>hello world</h1>
	{% for user in users %}
	<p>用户名:{{user.name}}----->邮箱:{{user.email}}</p>
	{% endfor %}
	</body>
	</html>

2.flask中集成flask-sqlalchemy使用

	# 第三方: flask-sqlalchemy 封装了用起来,更简洁
	安装:pip install flask-sqlalchemy
	'''借助于flask-sqlalchemy'''
	1 导入 from flask_sqlalchemy import SQLAlchemy
    2 实例化得到对象
    	db = SQLAlchemy()
    3  将db注册到app中
    	db.init_app(app)
    ------2,3 可以合并为db = SQLAlchemy(app)--------
    
    4 视图函数中使用session
    	全局的db.session  # 线程安全的
    5 models.py 中继承Model
    	db.Model
    6 写字段 
    	username = db.Column(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

八、Flask-Migrate的使用

	1.数据库肯定要自己创建
	2.创建表,增加删除字段,需要手动做处理,而django 有两个命令自动做
		python manage.py makemigrations	 # 记录变化
    	python manage.py migrate  #把变化同步到数据库
		-那有没有种方案,跟djagno一样,自动记录,自动迁移?
	
	3.第三方模块:flask-migrate,就能完成跟django一样,只是命令稍微有些不同
	# https://github.com/miguelgrinberg/Flask-Migrate/
	安装:pip install Flask-Migrate --upgrade
	
	4.使用步骤
		-导入
		from flask_migrate import Migrate
		-注册
		'需要先导入app和db才行'
		migrate = Migrate(app, db)  # flask就会多出几个命令
		'''
		flask --app manage:app db init  # 初始化,第一次执行,以后再也不执行了,它执行完,会出现一个migrations文件夹
		flask --app manage:app db migrate # django中的makemigrations 是一模一样
		flask --app manage:app db upgrade  # 跟django的migrate一样
		如果启动文件就是叫app则可以省略--app manage:app,直接flask db migrate这样使用即可,其他命令也是
		'''

flask中还有和django中的模块类似的模块,如cors、cache、restful等等,具体可自行查找了解


网站公告

今日签到

点亮在社区的每一天
去签到