当前位置:   article > 正文

【Python Web 框架】flask005---sqlalchemy快速使用、scoped_session实现线程安全、flask_sqlalchemy的快速使用、flask-migrate使用_flask sqlalchemy session

flask sqlalchemy session

回顾:

  • 1 pipreqs–>快速生成项目的依赖

  • 2 函数和方法

  • 3 threading包下的local类—》实例化得到对象—》多线程并发操作————>数据不会错乱

    -加锁
    -每条线程操作自己的数据
    其实字典和列表都不是并发安全的,多个线程同时往字典里面写数据,会乱;写数据的过程是先取到、再写、再给它,这3步叫临界区
    所以这3步,必须要做成原子性的,要加锁

  • 4 自己写–》兼容协程(协程是单线程内的并发)

  • 5 flask请求上下文:RequestContext–ctx:request,session,flash

  • 6 flask应用上下文:AppContext—> app_ctx:当前app,g

  • 7 flask请求上下文源码分析

     1 请求来了执行---》app()--->触发类的__call__--->self.wsgi_app(environ, start_response)--->
     2 ctx = self.request_context(environ)-->封装了request和session
     3 ctx.push()--->_request_ctx_stack.push(self)-->self是ctx
     4 _request_ctx_stack是LocalStack类的对象--》push
     5 LocalStack的push方法源码
    
    • 1
    • 2
    • 3
    • 4
    • 5
def push(self, obj):
    rv = getattr(self._local, "stack", None)
    if rv is None:
        self._local.stack = rv = []
        # self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}
    rv.append(obj)
    return rv
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
   6 LocalStack对像中的  _local--->init初始化出来的
		-self._local = Local() # 咱们自己写的可以多线程并发访问的Local

   7 _local={线程或协程id号:{stack:[ctx]},线程或协程id号:{stack:[ctx]}}
	_local.stack--》取stack的值,在不同协程下,取到的是自己的
  • 1
  • 2
  • 3
  • 4
  • 5
  • 8 在视图函数中使用request,session—》都是当此请求的request和session,但是我们使用了全局变量。打印的真的是当次请求的Request类的对象,但实际上request根本不是Request类的对象,LocalProxy类的对象,LocalProxy类重写了__str__

  • 9 print(request)的时候–》类的__str__

  • 10 LocalProxy把所有的魔法方法都重写了,因为它是个代理类

  • 11 request = LocalProxy(partial(_lookup_req_object, “request”))

     -init---》def __init__(self, local, name=None):--->object.__setattr__(self, "_LocalProxy__local", local)--->local就是偏函数
    
    • 1
  • 12 LocalProxy—》str—>_get_current_object()是LocalProxy类的方法

  • 13 LocalProxy._get_current_object()—》return
    self.__local()–》加括号执行偏函数—》
    partial(_lookup_req_object, “request”)()—>_lookup_req_object(‘request’)–

  • 14 返回了当前线程所在的ctx中的request对象

  • 15 request.method–>就是当前线程的request对象的method方法

  • 16 在视图函数中打印print(session)—>是当此请求的session

sqlalchemy快速使用

ORM框架:对象关系映射,把数据库中数据转成对象的过程
SQLAlchemy是一个基于Python实现的ORM框架,跟web框架无关,独立的
django的orm,sqlalchemy(大而重),peewee(小而轻)
(了解)国人开源的一个异步orm框架:GINO

Python界没有一个特别好的微服务框架:nameko
java微服务框架:dubbo(阿里开源),springcloud(一天下)
go微服务框架:grpc,go-zero,go-micro

pip install sqlalchemy

django中的原生sql
    ret = Author.objects.raw('select * from app01_author where nid>1')
    ret = Author.objects.raw('select * from app01_book where nid>1')
        强大的地方,Author表去查book都可以,一样的字段可以映射上,不一样的就没有
        很多人了解了这个写法之后,就不想写orm了,因为复杂的,orm写不出来

django中如何反向生成models
    公司里面已经有表了,现在根据表反向生成models
    python manage.py inspectdb > app/models.py  #会自动把表
    正常操作:模型表中新增字段---》两条命令---》同步到数据中
    特殊操作:模型表中新增字段---》不要了---》在数据中增加字段;此时只要对上就行了。但是后面不能执行那两条命令了,就会报错
    就是一旦不用,以后都不用
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

执行原生sql
代码

import time
import threading
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine

engine = create_engine(
    "mysql+pymysql://root:111@127.0.0.1:3306/cnblogs",  # root用户,密码是111,cnblogs是库名
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置),就是说5个链接,用多久之后会被回收,然后再生成再用
)  # pool_recycle=-1就是不重置


def task(arg):
    conn = engine.raw_connection()  # 创建原生的连接池
    cursor = conn.cursor()
    cursor.execute(
        "select * from article"
    )
    result = cursor.fetchall()
    print(result)
    cursor.close()
    conn.close()


for i in range(20):
    t = threading.Thread(target=task, args=(i,))
    t.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

创建和删除表

建表模型,一般建在models.py里面
在sqlalchemy中必须要以__tablename__ = 'users' 指定表名(不像django中meta)
所有的列都是Column
__table_args__:table的一些参数
双下划线开头的,在django里面都是写在Meta里面的
UniqueConstraint:联合唯一(django里面的Uniquetogether)

表模型需要继承
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

上面的表已经弄好了,需要把通过
把表同步到数据库中(连哪个库,哪个地址)
使用engin指定地址和库
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
def init_db():
    engine = create_engine(
        "mysql+pymysql://root:111@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管理的所有表,创建出来
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

    但是sqlalchemy原生:只能创建和删除表不能新增和删除修改字段,不能创建数据库
    (django里面只要把字段改成别的名字,迁移一下就行)
    但是flask sqlalchemy 加flask-migrate可以实现。有第三方库
  • 1
  • 2
  • 3

下面再看删除表

def drop_db():
    engine = create_engine(
        "mysql+pymysql://root:111@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管理的表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
    sqlalchemy原生:只能创建和删除表不能新增、删除修改字段,不能创建数据库(django也不能创建数据库,都是手动创建出来的)
  • 1

一对多、多对多

一对多关系:一个Hobby可以有多个人喜欢,关联字段写在多的一方,Person
  • 1

在这里插入图片描述

多对多关系

多对多关系:男孩和女孩约会,一个男孩可以约多个女孩,一个女孩可以约多个男孩
多对多要中间表
  • 1
  • 2

在这里插入图片描述

在这里插入图片描述

models.py代码

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

Base = declarative_base()


class Users(Base):
    id = Column(Integer, primary_key=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, nullable=True)

    __tablename__ = 'users'  # 数据库表名称
    __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),  # 联合唯一
        Index('ix_id_name', 'name', 'email'),  # 联合索引
    )

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


# 一对多关系:一个Hobby可以有多个人喜欢,关联字段写在多的一方,Person

class Hobby(Base):
    __tablename__ = 'hobby'
    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指的是tablename而不是类名,跟hobby表的id字段建立外键关系
    hobby_id = Column(Integer, ForeignKey("hobby.id"))

    # 跟数据库无关,不会新增字段,只用于快速链表操作
    # 类名,backref用于反向查询
    hobby = relationship('Hobby', backref='persons')


# 多对多关系:男孩和女孩约会,一个男孩可以约多个女孩,一个女孩可以约多个男孩

class Boy2Girl(Base):
    __tablename__ = 'boy2girl'
    id = Column(Integer, primary_key=True, autoincrement=True)  # autoincrement自增
    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)


class Boy(Base):
    __tablename__ = 'boy'

    id = Column(Integer, primary_key=True, autoincrement=True)  # autoincrement自增
    name = Column(String(64), unique=True, nullable=False)

    # 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以
    girls = relationship('Girl', secondary='boy2girl', backref='boys')

# 把表同步到数据库中(连哪个库,哪个地址)
# 使用engin指定地址和库
# 只能创建和删除表不能新增和删除修改字段,不能创建数据库
def init_db():
    engine = create_engine(
        "mysql+pymysql://root:111@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:111@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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104

sqlalchemy的基本增删查改

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from models import Users, Person, Hobby, Girl, Boy, Boy2Girl

# 单表的基本增删查改

# 第一步:得到engine对象
engine = create_engine("mysql+pymysql://root:111@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)

# 第二步:得到Session对象,当成一个类
Session = sessionmaker(bind=engine)

# 第三步:创建session对象
# 每次执行数据库操作时,都需要创建一个session
session = Session()

# 第四步:以后使用session来操作数据

##################### 增单个 #####################
# 先创建出一个user对象
lqz = Users(name='lqz', email='33@qq.com')
# 把对象增加到数据库中
session.add(lqz)  # 只能同时增一个
# 提交事务
session.commit()
# 把连接放回到池中
session.close()

##################### 增多个 #####################
lqz = Users(name='lqz1', email='333@qq.com')
egon = Users(name='egon', email='343@qq.com')
lyf = Girl(name='刘亦菲')  # 在Girl表中
# 把对象增加到数据库中
session.add_all([lqz, egon, lyf])
# 提交事务
session.commit()
# 把连接放回到池中
session.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

##################### 基本的查(查是最多的,现在先讲简单的) #####################

lqz=session.query(Users).filter_by(name='lqz').first()  # 查一个,返回Users对象
  • 1

在这里插入图片描述

这边不显示名字
在这里插入图片描述

重写__str__就可以了
在这里插入图片描述
在这里插入图片描述

重写__repr__就可以了
在这里插入图片描述

##################### 删除(查完再删) #####################
在这里插入图片描述

查一个返回对象,查多个,返回列表
在这里插入图片描述

##################### 修改 #####################

res=session.query(Users).filter_by(name='lqz1').update({'name':'lqz_nb'})   #必须放在一个字典里面改
session.commit()
session.close()


# 类似于原来的F查询,把表中字段取出来使用
# synchronize_session=False  表示字符串相加
res=session.query(Users).filter_by(name='lqz_nb').update({Users.name: Users.name + "099"}, synchronize_session=False)
session.commit()
session.close()

# synchronize_session="evaluate" 表示数字相加(如果转不成数据,其实有错,直接设为0)
session.query(Users).filter_by(name='1').update({"id": Users.id + 10}, synchronize_session="evaluate")
session.commit()
session.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

查询操作

查询所有

select * from users;
r1 = session.query(Users).all() #结果是一个列表
  • 1
  • 2

给字段重命名

select name as xx ,email from users;
r2 = session.query(Users.name.label('xx'), Users.email).all()
  • 1
  • 2

filter和filter_by的区别

filter传的是表达式

r = session.query(Users).filter(Users.id >= 10).all()
r = session.query(Users).filter(Users.id == 3).all()
  • 1
  • 2

filter_by传的是参数

r = session.query(Users).filter_by(name='egon').all()   #列表
r = session.query(Users).filter_by(name='egon').first() #一个数据对象
  • 1
  • 2

:value 和:name 相当于占位符,用params传参数

from sqlalchemy.sql import text
select * from users where id <224 and name = egon1
r = session.query(Users).filter(text("id<:value and name=:name")).params(value=10, name='egon1').all()
  • 1
  • 2
  • 3

自定义查询sql(了解)

r = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='egon').all()
  • 1

这个里面可以写更复杂的

条件

   ret = session.query(Users).filter_by(name='egon').all()
  • 1

表达式,and条件连接

ret = session.query(Users).filter(Users.id > 1, Users.name == 'egon').all() #id大于1,并且name是egon
select * from user where name=egon id between 1 and 3;
ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'egon') #id是1-3之间,并且name是egon
  • 1
  • 2
  • 3

注意下划线

ret = session.query(Users).filter(Users.id.in_([1,3,4]))    #id是1,3,4的任一个的
  • 1

~非:除。。外

ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all() #id是除了1,3,4之外的
  • 1

二次筛选

ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='egon'))).all()
  • 1

or_包裹的都是or条件
and_包裹的都是and条件 Q查询

from sqlalchemy import and_, or_
ret = session.query(Users).filter(and_(Users.id > 2, Users.name == 'egon')).all()
ret = session.query(Users).filter(or_(Users.id >10, Users.name == 'egon')).all()

ret = session.query(Users).filter(
    or_(
        Users.id < 2,
        and_(Users.name == 'egon', Users.id > 3),
        Users.extra != ""
    )).all()    #or里面的3个是或的条件,3个里面其中一个又是and条件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

通配符,以e开头,不以e开头

ret = session.query(Users).filter(Users.name.like('e%')).all()  #查出已e开头的所有的名字
ret = session.query(Users).filter(~Users.name.like('e%')).all()
  • 1
  • 2

限制,用于分页,区间

每页显示10条,拿第8页
  • 1
ret = session.query(Users)[(8-1)*10:10]
  • 1

排序,根据name降序排列(从大到小)

ret = session.query(Users).order_by(Users.name.desc()).all()    #.desc()可以不写
  • 1
第一个条件重复后,再按第二个条件升序排
ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc())
  • 1
  • 2

分组 (就是把一样的归成一类):一旦用了分组,查询的字段只能是分组字段和聚合函数的字段

from sqlalchemy.sql import func
# 原生sql:select * from users group by user.extra
  • 1
  • 2
例子:
1 egon  32@qq.com
2 egon  31@qq.com
3 lqz   34@qq.com
一旦用了分组,按名字分组,就成了两类;一类是egon。一类是lqz
但是名字是egon的,有两个id。两个邮箱,不知道拿谁,所以要取的字段必须是分组字段和聚合函数的字段
所以不能select id,不能select 邮箱,只能是select name(name就是分组字段,group by后面的)
select * from users group by user.name

ret = session.query(Users).group_by(Users.extra)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

聚合函数min max avg sum count
分组之后取最大id,id之和,最小id

select max(id),sum(id),min(id) from users group by users.name
ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name)
  • 1
  • 2
  • 3
  • 4
  • 5

haviing筛选
分组筛选完了,再去过滤
原生sql:select max(id),sum(id),min(id) as min_id from users group by users.name having min_id>2

django的orm:Users.object.all().value(Users.name).annotate(min_id=func.min(Users.id),max_id=func.max(Users.id),sum_id=sum(Users.id)).filter(min_id__gte=2).values(min_id,max_id,sum_id)
django的orm补充:
    filter在annotate前表示过滤
    value在annotate前表示分组的字段
    filter在annotate后表示having
    value在annotate后表示取字段

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

连表(默认用forinkey关联)

select * from person,hobby where person.hobby_id=hobby.id
ret = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id)
  • 1
  • 2

join表,默认是inner join(内连接)
面试题:左连接、右链接。内连接的区别?
左连接以左表数据为准、右链接以右边数据为准、内连接以共有的数据为准

select * from person inner join on person.hobby_id=hobby.id
ret = session.query(Person).join(Hobby)
isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可
ret = session.query(Person).join(Hobby, isouter=True)
ret = session.query(Hobby).join(Person, isouter=True)
  • 1
  • 2
  • 3
  • 4
  • 5

打印原生sql(去掉后面的.all())

aa=session.query(Person).join(Favor, isouter=True)
print(aa)
  • 1
  • 2

自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上

SELECT * FROM person LEFT OUTER JOIN hobby ON hobby.id = person.id
ret = session.query(Person).join(Hobby,Person.nid==Hobby.id, isouter=True)
  • 1
  • 2

组合(了解)UNION 操作符用于合并两个或多个 SELECT 语句的结果集

union和union_all的区别?
    答:union去重、union_all不去重

q1 = session.query(Person.name).filter(Person.nid > 0)
    结果:
        lqz
        lyf
        pyy
q2 = session.query(Hobby.caption).filter(Hobby.id > 0)
    结果:
        lqz
        egon
        lyf
ret = q1.union(q2).all()
    结果:
        lqz
        lyf
        pyy
        egon
ret = q1.union_all(q2).all()
    结果:
        lqz
        lyf
        pyy
        lqz
        egon
        lyf
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

sqlalchemy的关联操作

基于对象的跨表查:  book.publish
基于连表的跨表查:  __连表

一对多和多对多关系的新增和查询
  • 1
  • 2
  • 3
  • 4
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from models import Users, Person, Hobby, Girl, Boy, Boy2Girl
from sqlalchemy.sql import text

# 第一步:得到engine对象
engine = create_engine("mysql+pymysql://root:111@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)

# 第二步:得到Session对象,当成一个类
Session = sessionmaker(bind=engine)

# 第三步:创建session对象
# 每次执行数据库操作时,都需要创建一个session
session = Session()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

一对多关系

####################### 新增 #########################
p = Person(name='刘清政', hobby_id=1)
hobby = Hobby(caption='橄榄球')
session.add_all([p, hobby])
session.commit()

####################### 新增方式二 #########################
p = Person(name='egon', hobby=Hobby(caption='保龄球'))
session.add_all([p, ])
session.commit()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

####################### 基于对象的跨表查询  --->正向查询 #########################
lqz=session.query(Person).filter_by(name='刘清政').first()
print(lqz.hobby_id)
print(lqz.hobby.caption)

####################### 基于对象的跨表查询的反向查询 #########################
lq = session.query(Hobby).filter_by(caption='篮球').first()   #拿到篮球的对象
print(lq.persons)   #查到所有喜欢篮球的人
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

多对多关系

一对多的时候,关联关系写在多的一方。多对多的时候,根本就没有这个字段,是一个中间表。写在哪一方都可以
  • 1

在这里插入图片描述

####################### 快速新增,前提是Boy表中有girls字段 #########################
  • 1
boy = Boy(name='小王', girls=[Girl(name='小花'), Girl(name='小华')])
session.add(boy)
session.commit()


# 如果没有这个字段,就需要这样新增
####################### 笨办法 #########################
boy = Boy(name='小刚')
girl1 = Girl(name='小静')
girl2 = Girl(name='小月')

session.add_all([boy,girl1,girl2])
session.commit()
b1=Boy2Girl(girl_id=4,boy_id=2)
b2=Boy2Girl(girl_id=5,boy_id=2)
session.add_all([b1,b2])
session.commit()

####################### 基于对象的跨表查  正向 #########################
xg=session.query(Boy).filter_by(name='小刚').first()
print(xg.girls)

####################### 反向 #########################
xj=session.query(Girl).filter_by(name='小静').first()
print(xj.boys)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

基于scoped_session实现线程安全

当我们把项目继承到flask里面之后,
# 每次执行数据库操作时,都需要创建一个session
session = Session()
这个session就是一个全局的变量,就会出现多个线程公用这一个连接,scoped_session解决了这个问题
来一个新的线程都会创建一个scoped_session(有threading.local())
  • 1
  • 2
  • 3
  • 4
  • 5
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:111@127.0.0.1:3306/aaa", 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'
# )

# scoped_session类并没有继承Session,但是却又它的所有方法。是动态的一个一个的写进去的
# session=Session()
session = scoped_session(Session)

# ############# 执行ORM操作 #############
obj1 = Users(name="egon111")
session.add(obj1)

# 提交事务
session.commit()
# 关闭session
session.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

sqlalchemy集成到flask中

  	from flask import Flask, jsonify
    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:111@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = scoped_session(Session)

    app = Flask(__name__)


    @app.route('/<string:name>')
    def index(name):
        egon = session.query(Users).filter_by(name=name).first()
        egon_dic = {'name': egon.name, 'email': egon.email}
        return jsonify(egon_dic)


    if __name__ == '__main__':
        app.run()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
上面是自己实现的,第三方帮我们做了,使用flask_sqlalchemy集成到flask中
我们上面学的是sqlalchemy的使用,它可以用和任何框架结合起来,甚至django,只是没有人这样用
  • 1
  • 2

在这里插入图片描述

flask_sqlalchemy的快速使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用的就是db.session、和db.model
  • 1

使用步骤

    1 导入from flask_sqlalchemy import SQLAlchemy 实例化得到db对象
    2 在app中注册
        db.init_app(app)
    3 表模型继承 db.Model
    4 session是db.session
        db.session使用即可
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

存在问题

    1 表迁移麻烦
    2 不支持字段的动态修改
  • 1
  • 2

flask-migrate使用(就和插件差不多了)

像djagno一样,执行两条迁移命令,实现数据库的动态迁移
pip install flask-migrate
  • 1
  • 2

使用步骤

    第一步:from flask_migrate import Migrate, MigrateCommand
    第二步:执行
    Migrate(app, db)
    manager.add_command('db', MigrateCommand)

python3 manage.py db init 初始化:只执行一次,生成一个migrations文件夹
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

以后直接在model.py里面新建表,新建字段,执行命令,就会自动同步
    python3 manage.py db migrate 等同于 makemigartions
    python3 manage.py db upgrade 等同于migrate
  • 1
  • 2
  • 3

接口幂等性

接口幂等性:http://www.liuqingzheng.top/article/1/01-%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%B9%82%E7%AD%89%E6%80%A7/
幂等性:多次操作,做的改变是一样的
新增接口--》执行了3次,出现3条记录,这就不是幂等性的接口
get,delete,update 都是幂等性 (查一次跟查10次是没有区别的,删除1次跟删10次也是没有区别的)
post:是不幂等的;存在问题,就是说是用户真的想创建两个订单,还是不小心点了两次
如果要保证幂等,使用token机制(只要访问之前,先拿一个token值,放在redis里面。想要创建,必须携带这个token,创建完,把redis删除。下次再带同样的token过来,redis里面没有了
就代表消费过了,就不能再创建这个订单了)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

云原生:

云:云服务器(公有云:阿里,华为,私有云:公司自己的服务器)
原生:使用编程语言写的程序(go,python,java写了程序)
把这些程序,跑在云上,就叫云原生
云原生的组件:docker,k8s,jenkins,gitlab。。。。(主要指的是k8s)
  • 1
  • 2
  • 3
  • 4

云存储

就是存储文件的服务器,是集群架构的
公有云存储:七牛云,阿里oss
私有云存储:fastdfs,ceph,minio。。
  • 1
  • 2
  • 3

paas,saas,iaas(云服务的模型)

SaaS(软件即服务):假设要搞一个电商,之前是要招开发,开发完部署,上线使用。现在是直接买软件,软件也跑在软件公司的服务器上,不需要部署在客户现场。就是直接买软件
PaaS(平台即服务):云平台,阿里云就是paas,买云服务器,上面改装的软件都装好了,我只需要把我的服务跑在上面
IaaS(基础架构即服务):买服务器的厂商,你买了之后,自己组装,装上软件,自己跑
  • 1
  • 2
  • 3

serverless:无服务

现在公司要干这个事,直接用别人的
  • 1

敏捷开发

敏捷开发的一个sprint
  • 1

devops ,sre

其实是一个概念,不同公司提出来的
运维+开发 保证云平台稳定运行---》sre,devops
  • 1
  • 2

ci cd jenkins+gitlab+docker

持续集成
持续部署
  • 1
  • 2

微服务

服务拆分----》分布式的子集
  • 1

中间件

组件:
消息队列中间件:rabbitmq,kafka
数据库中间件(程序和数据库之间:做一些分库分表的):mycat
服务器中间件(程序和服务之间的一个东西):nginx,kong---》api网关
缓存中间件:redis
  • 1
  • 2
  • 3
  • 4
  • 5

DDD:领域驱动设计

只是一个概念
核心的逻辑写在数据层
  • 1
  • 2

回顾

1、sqlalchemy orm框架

单表

一对多
    一对一,是一对多的特殊情况
    Person  Hobby两张表,一对多的关系一旦确立,外键字段写在多的一方
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

    就是为了模拟出django的orm中基于对象的跨表查询,直接点hobby就能点出hobby对象来
多对多:需要建中间表
    表中除了需要id之外,还需要记录额外的信息,这时就必须手动创建第三张表了
    中间表里其实就是跟第一个表和第二个表做一个外键关联
  • 1
  • 2
  • 3
  • 4

2、sqlalchemy

不能创建库,不能修改字段
  • 1

3、flask-sqlalchemy

直接用的话,一步一步的用,但是框架又帮我们封装了一下,实例化得到一个对象
使用步骤
    1 导入from flask_sqlalchemy import SQLAlchemy 实例化得到db对象
    2 在app中注册
        db.init_app(app)
    3 表模型继承 db.Model
    4 session是db.session
        db.session使用即可
Sqlalchemy---》db.session和db.Model
配置信息配置在配置文件中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4、现在创建表,修改表,创建字段。比较麻烦,于是就有了flask-migrate

flask-migrate :基于flask-script
flask-script 自定义命令
flask-migrate借助于flask-script创建了一个db命令
python3 manage.py db init  执行一次--》
python3 manage.py migrate   记录
python3 manage.py upgrade   同步到数据库
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/613222
推荐阅读
相关标签
  

闽ICP备14008679号