kaixin
Published on 2023-04-23 / 44 Visits
0

Falsk框架

Falsk框架?

内部采用了
1.模板引擎jinja2
2.werkzeug路由
3.itsdangrous token加密模块
4.click 命令终端
5.flask本身
其他的需要必须进行扩展自己安装

Flask的扩展包?

Flask-SQLalchemy 数据库操作orm
Flask-RESTful:api框架
script 插入脚本(淘汰)
migrate 管理迁移数据库
Session:session储存方式制定
Login:认证用户状态 (django内置auth模块,用户实现退出登录)
OpenID 认证oauth 第三方授权
JSON-RPC 远程调用
Mail:邮件
WTF:表单生成模块 基本不用 防御机制
Banble:提供国际化本地化支持
OpenID:认证
Bootstrap:继承前端 booststrap框架
Moment:本地化日期和时间
Admin:简单而扩展的管理接口的框架 需要配合booststrap框架
marshmallow 序列化器 类似于django的序列化器

包的中文文档总结
https://wizardforcel.gitbooks.io/flask-extension-docs/content/index.html

Flask生命周期

客户端(http)->web服务器(转发协议)->wsgi(把请求协议转为request对象)
->全局钩子->路由->视图->路由->全局钩子->wsgi应用(把response转为响应文本)
->web服务器(http响应)->客户端

Flask开始

Flask-基本使用

1.创建虚拟环境
virtualenv 虚拟环境名称 --python=python3.9 # 指定python包
2.在pcharm中配置虚拟环境,直接就进入了虚拟环境的状态
3.也可以通过
activate 进入
deactive 退出


# 导入文件类
from flask import Flask

# 初始化 __name__ 拿到的就是当前py的文件名称入口名称
app = Flask(__name__)


@app.route('/') # 可以通过route装饰器 绑定路由和函数的关系
def index():
    # 视图的名字不能重复,重复报错
    # 视图的返回值包装为html内容返回给客户端
    return '测试'  # 支持html内容


if __name__ == '__main__':
    # 运行 相当于django的runserver
    # host 路由 debug 开启debug模式 port端口设置
    app.run(host='120.0.0.1', debug=True, port=5000)

Flask中的对象参数?

app = Flask(__name__) 内部的参数

import_name: # flask启动项目的包模块 传入__name__就可以,决定flask在访问静态文件的查找路径

静态文件的url路
static_url_path:默认为 /static
   # static_url_path='/python',
   # 访问: http://127.0.0.1:5000/python/123.png
   # 默认访问: http://127.0.0.1:5000/static/123.png 如果不设置的情况下默认参数stati

静态文件的存储文件夹 默认static
static_folder: t.Optional[t.Union[str, os.PathLike]] = "static" 
  

模板文件夹 可以不传默认templates
template_folder: t.Optional[str] = "templates",

host 路由默认值 debug 开启debug模式 port端口设置有默认值
app.run(host='120.0.0.1', debug=True, port=5000)

Flask项目加载配置方法?

1.可以通过key val字典格式添加站点配置
app = Flask(__name__)
app.config['DEBUG'] = True # 添加配置 传入大写
可以写入的参数必须大写
配置信息都放在这里面(按照 字典类型处理是可以的)
{
    'ENV': 'production',
    'DEBUG': True,
    'TESTING': False,
    'PROPAGATE_EXCEPTIONS': None,
    'SECRET_KEY': None,
    'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31),
    'USE_X_SENDFILE': False, 
    'SERVER_NAME': None,
    'APPLICATION_ROOT': '/', 
    'SESSION_COOKIE_NAME': 'session', 
    'SESSION_COOKIE_DOMAIN': None,
    'SESSION_COOKIE_PATH': None,
    'SESSION_COOKIE_HTTPONLY': True, 
    'SESSION_COOKIE_SECURE': False,
    'SESSION_COOKIE_SAMESITE': None, 
    'SESSION_REFRESH_EACH_REQUEST': True, 
    'MAX_CONTENT_LENGTH': None,
    'SEND_FILE_MAX_AGE_DEFAULT': None, 
    'TRAP_BAD_REQUEST_ERRORS': None, 
    'TRAP_HTTP_EXCEPTIONS': False,
    'EXPLAIN_TEMPLATE_LOADING': False, 
    'PREFERRED_URL_SCHEME': 'http', 
    'JSON_AS_ASCII': None, 
    'JSON_SORT_KEYS': None,
    'JSONIFY_PRETTYPRINT_REGULAR': None, 
    'JSONIFY_MIMETYPE': None, 
    'TEMPLATES_AUTO_RELOAD': None,
    'MAX_COOKIE_SIZE': 4093
}

2.加载站点配置的方式 字典方式加载的方式进行加载
app = Flask(__name__)
config = {
    'DEBUG':True
}
app.config.update(config)

3.类方式加载
app = Flask(__name__)
class Config:
    DEBUG = True
app.config.from_object(Config)

4.文件方式加载
在文件中编写对应配置
config.py
    DEBUG=True 
    
app = Flask(__name__)
app.config.from_pyfile('config.py') #(文件路径)

5.从环境变量中进行加载
... 见官方

Flask-路由使用

路由是一种 视图与路由的1对1的映射关系


# 路由和视图全局唯一
路由重复,只会显示第一个路由的试图
视图重复会报错
# 不设置methods 的话 默认get请求
@app.route(rule='/', methods=['GET','POST'])
def index():
    '''
    rule 路由的写法 可以 rule = / 也可以 /
    methods = ['POST','GET'] 允许多个请求方式或者单个请求方式 控制请求方式
    :return:
    '''
    return '测试'  # 支持html内容


app.url_map # 返回全部的项目中的url 是一个flask封装的map对像
app.url_map.iter_rules() # 返回一个迭代器可以进行循环
form item in app.url_map.iter_rules():
     item.rule # 是路由对象
     item.endpoint # 是当前路由的视图名称	

Flask-路由参数

@app.route(rule='/index/<id>', methods=['GET'])
def index2(id):
    '''
    路由传递(单个)
    127.0.0.1:8000/index/123
    :param id: 接受路由传入的参数id
    :return:
    '''
    return '当前传入的参数是%s' % id


@app.route(rule='/index/<id>/<cid>', methods=['GET'])
def index3(id, cid):
    '''
    路由传递参数多个
    127.0.0.1:8080/123/456
    :param id: 第一个参数
    :param cid:  第二个参数
    :return:
    '''
    return '当前传入的参数是%s-%s' % (id, cid)

# 接受的参数默认是 字符串

路由转换器

1.默认
    string any path int float uuid
    对路由传入的参数的限定格式转换 默认字符串
    int 整形
    uuid uuid类型
    string 字符串
    float 浮点性
    path 路径格式 例如:asdas/sadada
    @app.route('/index/<int:id>/<int:cid>', methods=['GET'])
    def index(id, cid):
        print(type(id), type(cid)) # int类型
        return 'hello word'

2.自定义路由转换器(限制一些访问规定)
# 1.导入路由转换器父类
from werkzeug.routing.converters import BaseConverter 
# 2.创建类
class MobileConverter(BaseConverter): 
    regex = r'1[3-9]\d{9}' # 必须是正确的手机号格式
    
# 3.注册自定义路由转换器
app.url_map.converters['mobile'] = MobileConverter 

# 4.使用路由转换器
@app.route('/index/<mobile:cid>', methods=['GET']) 
def index(id,cid):
    pass


3.动态设置路径规则更加的灵活	# (高级用法)
# 1.导入路由转换类
from werkzeug.routing.converters import BaseConverter 

# 2.创建路由转换类
class MobileConverter(BaseConverter):
    '''
    map : 路由对象
    args :接收位置参数
    kwages :接收关键字参数
    '''

    def __init__(self, map, *args, **kwargs):
        super().__init__(map, args, kwargs)
        self.regex = args[0]
        
        
# 3.注册路由转换类
mobile 相当于 MobileConverter类的别名 指向当前MobileConverter类
app.url_map.converters['mobile'] = MobileConverter


# 4.使用路由转换类
 动态设置定路由规则:
    <mobile(name = r"1[3-9]\d{9}"):msi>  会被mobile注册的自定义路由规则类的kwargs给接收 {'name':'1[3-9]\\d{9}'}
    <mobile(r"1[3-9]\d{9}"):msi>  会被mobile注册的自定义路由规则类的args给接收 ( '1[3-9]\\d{9}',)
# mobile(r"1[3-9]\d{9}")相当于给注册的类进行实例化   
@app.route('/index/<mobile(r"1[3-9]\d{9}"):msi>', methods=['GET'])
def index(msi):
    return 'hello word'

Flask请求与响应参数

请求参数(接收的内容)

from flask import  request # 全局对象


1.request.files  # 记录上传的文件列表(表单) ImmutableMultiDict
request.files.get('文件key') # 获取单个
request.files.getlist('文件key') # 获取同key下的多个返回列表
使用FileStorage对象提供的save方法,从内存中将图片存储到本地
    file = request.files.get('file') # 获取
    file.save('./file.png') # 保存

3.request.headers # 获取请求头


4.request.data # 记录请求体的数据并且转为字符串  字节b'' 原始数据其他的数据如:xml
request.data  # 需要进行转换数据才能使用
json.loads(request.data) # 转换为正常dict类型


5.request.form # 记录html的表单数据(表单) ImmutableMultiDict
request.form.get('参数') # 获取单个
request.form.getlist('参数') # 获取同一个key的多个值 列表


6.request.args or request.query_string
# 记录url的查询字符串 也可以使用query_string ImmutableMultiDict
路由:http://127.0.0.1:8080/?name=666&name=777
request.args : ImmutableMultiDict([('name', '666')]) 对象字典get取值
request.query_string : b'name=666' 字节
request.ages.get('name') # 只能获取单个值(第一个值) 666
request.args.getlist('name') # 可以获取多个值 ['666','777']


7.request.method
# 记录http的请求方法(获取url的请求格式)
路由:http://127.0.0.1:8080/
request.method :GET


8.request.json # 获取ajax的json数据
request.json # 返回的不同的dict数据


9.request.is_json # 判断传入数据是不是ajax请求json格式
request.is_json 返回 True/Fasle


10.request.path # 获取当前访问地址
获取当前访问的路径不加端口和ip

11.request.url  # 获取请求的完整url

12. request.environ # 获取环境变量 

# 可以使用dir(request) #查看方法

响应参数(返回的结果)

1.数据响应
from flask import Flask, make_response, Response, jsonify
    1.响应html
    在视图中直接 return 文本 响应的就是html代码
    # 直接返回元祖(响应内容,响应状态码)
    return 'hello flask',201(返回状态码)  # 响应内容并且返回状态码

    # 通过make_response返回响应对象
    return make_response('hello flask',201)

    # response 返回响应对象(最底层的相应)
    return Response('hello flask',201)

    2.响应json
    # 使用原生的json模块(如果不添加Content-Type会被当成html) 不建议使用麻烦
    data = {'name':'小明'}
    return json.dumps(data),200,{'Content-Type':'application/json'}
    
    # 使用flask提供的方法jsonify
    data = {'name':'小明'}
    jsonify(data)
    
    3.响应媒体图片
    需要设置{'Content-Type':'image/png'} 就可以进行返回
      with open('file.png','rb') as f:
        data = f.read()
    return data,200,{'Content-Type':'image/png'} # MIME类型
    
    
2.页面响应 链接或者重定向
from flask import Flask, request,redirect,url_for

    1.站内跳转
    @app.route(rule='/', methods=['get', 'post'])
    def index():
        if request.args.get('token'):
            return '个人中心'

        # 1.基于redirect 参数就根据url地址,跳转到任意路径下
        return redirect('/login')

        # 2.基于url_for 根据属视图名称跳转到当前视图路径下
        # 根据flask 的路由表生成的 app.url_map()
        return redirect(url_for('login'))

    @app.route('/login')
    def login():
        return '登录s视图'
    
    2.站外跳转
    '''
    301:永久重定向
    302:临时重定向
    '''
    return redirect('https://www.baidu.com',302)

    3.带参数跳转
     1.使用redirect方式将参数携带过去到/sms/路由下
        return redirect('/sms/13027630227')

        @app.route(rule='/sms/<int:ppp>')
            ......
        
     2.使用url_for携带参数跳转
        # url_for('视图名称',视图参数='传入的参数')
        @app.route(rule='/')
        def index():
            url = url_for('sms',ppp='13027630227') # 进行了拼接
            return redirect(url)

        @app.route(rule='/sms/<int:ppp>')
        def sms(ppp):
            return '给%s发送短信' % ppp
        
        
3.自定义响应头
内容,状态码,响应头
return 'hello flask',201,{'Company':'flask'} # 自定义响应头不能中文

Flask-Cookie与Session

保护用户的状态,点击浏览器开启,关闭浏览器关闭
http无状态协议,浏览器请求服务器是无状态
无状态:
    指一次用户请求时,浏览器 服务器无法知道这个yoghurt做了什么对于服务器而,客户端每次请求都是一个新的请求

无状态原因:
    浏览器与服务器是socket套接字通信,服务器将请求的结果数据返回给浏览之后,会关闭当前的链接,而客户端也会处理网页面后销毁页面对象,
    主要是为了保存确认用户是否登录,浏览过那些商品
    
状态保存:
    1.在客户端存储 cookie token【jwt,oauth】
    2.在服务器保存 session,数据库
    
每次请求都会将cookie发送给服务器

from flask import Flask, make_response, request
@app.route(rule='/set_cookie')
def set_cookie():
    # 通过make_response设置
    # 在响应对象中进行设置cookie max_age= cookie的过期时间按照秒
    response = make_response('cookie设置')
    response.set_cookie('user_id', '1')
    response.set_cookie('user_name', 'wkx', max_age=3600)
    return response


@app.route(rule='/get_cookie')
def get_cookie():
    # 通过request获取cookie
    user_id = request.cookies.get('user_id')
    user_name = request.cookies.get('user_name')
    print(user_id, user_name)
    return "cookie获取"


@app.route(rule='/del_cookie')
def del_cookie():
    # cookie保存在客户端的,无法直接删除cookie
    # 只能告诉浏览器过期了内容设置为空,让浏览器自动删除,让时间变为0
    response = make_response('cookie删除')
    response.set_cookie('user_id', '',max_age=0)
    response.set_cookie('user_name', '', max_age=0)
    return response

Session

session依赖于cookie 加密保存到session中
基于token的方式存放在客户端中

from flask import Flask, session

app.config['SECRET_KEY'] = '加密' # 基于cookie设置需要设置秘钥

@app.route(rule='/set_session')
def set_session():
    """设置session"""
    session['username'] = '4164646'
    session['userid'] = '4164646'
    return 'set_session'


@app.route(rule='/get_session')
def get_session():
    """获取session"""
    username = session.get('username')
    userid = session.get('userid')
    print(userid,username)
    return "get_session"


@app.route(rule='/del_session')
def del_session():
    """删除session"""
    session.pop('username')
    return 'del_session'




# 注意:
    在设置flask配置中得参数'PERMANENT_SESSION_LIFETIME'需要配置session.permanent = True才会生效
    session.permanent = True # 设置这个参数 session的过期时间才会生效

FLASK-全局钩子

中间件

作用:
1.在项目启动时,建立数据库链接,或者创建连接池
2.在客户端请求开始时,对用户进行身份识别权限校验
3.在请求视图返回数据的时候,指定转换数据格式或者记录操作日志

1.before_first_request 
    在处理第一个请求前执行,项目被运行第一次被客户端请求执行的钩子
    # 数据库初始化 加载一些可以延后引入的全局配置

@app.before_first_request # 写法1 2.3之前使用
def before_first_request():
    """启动项目时首次被访问 @app.before_first_request 全部的函数(只会执行一次作为初始化使用)"""
    print('执行当前before_first_request')

app.before_first_request_funcs.append(before_first_request) # 写法2 2.3之后使用
    
    
    
2.before_request 
    在每一次客户端请求来之前都会进行执行
    
@app.before_request
def before_request():
    """每次在请求之前会执行,只要请求视图都会执行 before_request装饰的函数(每次访问视图之前的操作)"""
    print('执行当前before_request')
    
    
    
3.after_request
    如果没有抛出错误情况下,每一次请求执行视图结束后,就会直行
    接收一个参数,视图的相应参数
    # 加工一些响应头返回
@app.after_request
def after_request(response):
    """每次客户端访问之后,都会执行after_request装饰的函数(因为在视图后,需要将响应体返回)
    返回数据加工或格式转换 日志记录
    response:响应体
    """
    print('after_request')
    response.headers['....'] = .....
    return response
    
    
    
4.teardown_request
    每一次请求后都会执行,接收一个错误的信息,只有debug=false 才会接收异常参数

@app.teardown_request
def teardown_request(error):
    """
    在客户端访问服务端报错后 获取执行错误的结果
    就会自动执行teardown_request装饰的函数 接收一个error 本次结果的异常
    error:本次出现的异常
    在2.2之前 DEBUG = False才会执行teardown_request装饰函数
    """
    print('after_request')
    print(error)
    
    

Flask-抛出异常

分离不分离都可以使用

1.主动抛出异常 abort 是主动的将异常抛出
参数1:响应状态码
参数2: 页面的提示信息
from flask import Flask, abort

app = Flask(__name__)
app.config['DEBUG'] = True


@app.route(rule='/')
def set_session():
    print('执行视图')
    abort(401,'呵呵呵权限有问题呦') # 元祖方式
    abort(401,{'error':123456}) # 字典方式
    return '执行视图'

2.捕获错误信息
通过app.errorhandler装饰器进行捕获
当程序处理错误状态码时,就会调用该装饰器返回错误信息
app.errorhandler('异常类') 参数可以接收一些抛出的异常类
app.errorhandler('状态码') 也可以接收一些状态码

1.被动抛出异常被捕获到
@app.route(rule='/')
def set_session():
    try:
        print(hello)  # 出现异常
    except Exception as e:
        raise NameError(e)  # 抛出异常
    return '使用try捕获异常并且抛出异常'
    

@app.errorhandler(NameError)  # 接收异常
def NameErrorFunc(exc):
    """
    针对与变量命名异常
    :param exc: 接收的错误信息 异常的实例对象
    :return: 错误信息的状态
    """
    print(dir(exc))
    print(exc.__traceback__)  # 提示的对象 可以循环进行打印
    print(exc.args)  # 获取抛出的描述信息(元祖) 拿不到abort错误描述
    return {'code': 500, 'error': str(exc)}  # 返回json结构的数据


2.主动抛出异常
@app.route('/error')
def error():
    abort(400, '视图异常')
    return '主动抛出异常'

@app.errorhandler(400)
def Func_400(exc):
    """出现400错误那么就会 调用当前装饰器的函数抛出异常"""
    print(dir(exc))
    print(exc.code)  # 拿到abort错误的状态码
    print(exc.description)  # 拿到abort的错误描述
    return {'code': 400, 'error': str(exc)}


# 关于状态码问题:
@app.errorhandler(404)
如果不是abort主动抛出的异常 也会被捕获到 
也可以是系统抛出的异常,也会被捕获到
404 访问不存在的路由就会被捕获到


# 关于raise抛出异常
class 自定义的error类(Exception):
    pass

raise  自定义的error类('自定义错误')
    
@app.errorhandler(自定义的error类) # 也能捕获到

Flask-终端启动方式

关于flask开发阶段

app.config
在开发环境下  ENV = production

# 注意:
# 设置终端启动命令时,需要进行当前启动flask 的入口目录下进行设置

1.linux环境

# 需要在命令行设置
export FLASK_ENV = development # linux下设置的2.3环境不适用

export FLASK_DEBUG = True # 2.3
# 设置run 启动
export FLASK_APP = 启动文件的py文件 # linux下启动

可以通过
flask run --host 0.0.0.0 --port 5000 进行启动

2.win环境
set FLASK_ENV = development # 2.3版本以下
set FLASK_APP = 启动文件的py文件
set FLASK_DEBUG = True # 2.3 设置生产环境还是开发环境
可以通过
flask run --host 0.0.0.0 --port 5000 进行启动

其他命令
flask  db   [命令] 执行数据库迁移
flask routes  显示程序的路由
flask run 运行开发服务器
flask shell 在程序上下文运行shell  exit() # 退出


生产环境
FLASK_DEBUG = FLASE
FLASK_APP = 启动文件的py文件

给 uwsgi使用 flask run --host 0.0.0.0 --port 5000 进行启动

其他命令
flask routes  查看当前服务下的路由信息
flask shell 进入交互模式 进入上下文


自定义终端命令

from flask import Flask
import click # 必须导入当前包

app = Flask(__name__)
app.config['DEBUG'] = True


# 自定义添加命令到命令终端中
@app.cli.command('faker') 
@click.argument('data',type=str, default='user')
@click.option('-n', 'number', type=int, default=1, help='生成的数据量') 
def flask_command(data, number):
    print('添加测试参数')
    print('生成数据类型%s' % data)
    print('生成数据量:number=%s' % number)


@app.route(rule='/')
def set_session():
    return '使用try捕获异常并且抛出异常'


if __name__ == '__main__':
    app.run(port=8080, )

解释:
    1.需要在终端中设置了FLASK_APP,就可以进行使用
    2.相当于一个自定义的终端命令 与 flask run 一样 falsk faker(就执行)
    app.cli.command('faker') 
    
    
    1.接收的位置参数
    2.1.位置参数的名称 2.位置参数的类型(int,bool,srt,list) 3.位置参数的默认值
    3.执行 flask faker 1(位置参数)  # 那么位置参数 1 就会被data接收 (有几个位置参数那么就要在终端命令中写几个)
    4.需要几个位置参数就设置几个名称不能为python固定名称不能重复
    click.argument('data',type=str, default='user')  1 
    click.argument('data2',type=str, default='user2') 2
    ....
    ....
    
    
    1.选项参数
    2.1.简写的命令 2.选项参数名称 3.类型 4.默认值 5.说明
    3.执行时 flask faker 1(位置参数) -n 123456(选项参数) # 那么选项参数会被number接收
    click.option('-n', 'number', type=int, default=1, help='生成的数据量')
    
    
    1.函数
    def flask_command(data, number):
        """这里的内容作为自定义命令的说明"""
        data:接收的位置参数
        number:接收选项参数
        # 自定义的逻辑 可以实现如 django一样 django-damin startapp 新app程序 创建
        
   那么在终端中可以使用
   flask 查看注册的自定义命令是否成功

Flask-上下文概念

flask 的上下文对象有两种:
作用:相当于一个容器,保存flask程序在运行中的一些(变量 函数 类 对象信息)

1.请求上下文对象
    request context
    
2.应用上下文对象
    application context

application:指定时当服务端调用app=Flask(__name__)创建这个对象app
request:当每次发送http请求时 wsgi server调用 Flask.__all__()(将类作为一个函数加()执行),在falsk内本次客户端中创建request对象

application 表示响应wsgi请求的应用本身,request表示服务器每次响应客户端的http请求
application的声明请求周期大于request,一个application存活的期间,发生多次http请求,会存在多个request对象

请求上下文对象

请求上下文的对象:
    request,session
当前的请求上下文对象是在每一次请求时都会进行刷新,所以内部的数据是不同的
只有客户端访问服务器时才会产生请求上下文对象

request
    封装http请求的内容,主要针对http请求 如 user = request.ages.get('user') 获取get请求
    
session
    用来记录会话的信息,主要针对用户的信息,session['name'] = user.id 用来记录用户的状态信息,同时可以通过session.get('name')获取

# 注意:请求上下文提供变量和方法函数类对象,只能在视图中或者被视图调用的地方使用,没有发生客户端请求时,会报错超出上下文请求范围

def test():
    print(request) # 请求上下文对象【request|session】 只能被视图调用 或者 间接调用使用 


@app.route(rule='/')
def set_session():
    print(request)
    print(session)
    test()
    return '使用try捕获异常并且抛出异常'

应用上下文对象

帮助reqeust获取当前flask的引用相关信息,ta是伴request而生的,随request而灭

应用上下文:
    current_app,g # 可以 进行写入一些变量到里面 current_app.age = 18 ,在其他的视图中是可以调用的
    
# current_app: 为了方便request请求时使用当前的app对象
app=Flase(__name__) 创建时
current_app 同时创建的current_app代理对象 # 属于视图内部调用的对象
app is current_app # False 内存地址不相同
app == current_app # Ture 同一个对象
如果在视图外部使用当前的current_app对象需要构建应用上下文环境
对当前对象只能在 视图中使用或者间接使用
with app.app_context(): # 才能使用
   print(current_app)  


# g 全局数据存储对象 用于保存服务端存储的全局变量数据 基于客户端的全局变量 (只限于本次请求)
from flask import  g
def test():
    print(g.name) # 获取数据


@app.route(rule='/')
def set_session():
    print(g)
    g.name = 'wkx' # 存储数据
    test()
    return '使用try捕获异常并且抛出异常'

# 每个客户端都有自己的全局变量g g对象是隔离的

请求上下文与应用上下文区别

请求上下文:保存了客户端和服务端交互的数据,http的请求
应用上下文:flask应用程序运行时,保存的一些配置信息,路由 程序名 数据库链接 应用信息
应用上下文提供对象,可以直接在请求上下使用,如果请求上下文进行外调需要执行
with app.app_context() 创建一个上下文的环境才能使用

Flask-模板引擎JinJa2

需要使用falsk提供的render_template封装了模板引擎
render_template('模板的文件名','键值对(模板的使用数据)')


1.需要在创建app时指定模板引擎的文件
app = Flask(__name__,template_folder='templates')
# 如果不指定那么默认就是template文件

2.render_template,render_template_string 区别
2.1.render_template
    # 根据参数1文件路径,读取html模板内容,返回渲染后的html页面
2.2.render_template_string 
    # 基于参数1的的内容渲染模板 优势不用前端修改,直接在数据库中修改
    render_template_string ('str内容不识别模板路径识别字符串')
    例如:
    @app.route(rule='/')
    def set_session():
        title = '网页标题'
        content = '网页正文内容'
        tpm = """
         <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
            <h1>{{ title }}</h1>
            <p>{{ content }}</p>
        </body>
        </html>
        """
        html = render_template_string(tpm, **locals())
        print(html)
        return html
    

3.使用模板语法传入内容
@app.route(rule='/')
def index():
    return render_template("index.html",title='网页标题')

<h1>{{ title }}</h1> # 使用传入的键值对title变量


4.多个内容 **locals() 将多个键值传入模板
@app.route(rule='/')
def index():
    title = '网页标题'
    content = '网页正文内容'
    return render_template("index.html",**locals())

# 在index.html进行使用
<h1>{{ title }}</h1>
<p>{{ content }}</p>

模板语法

1.输出代码块

{{ 变量名 }} # 可以输出 dict set 元祖 str int bool 等等基本类型 复杂类型
例如复杂类型:
dic = {
        'name':123,
        "age":456
    }
    
<p>{{dic.get('name')}}</p> # 可以使用python的内置类型的原生方法进行取值,list,set,都可以使用
<p>{{dic.name}}</p> # 也可以使用.语法进行取值(js语法一样)

{{ 也可以渲染flask内的对象 }}
from flask import request,session,g 等等
<p>{{request}}</p>
<p>{{session}}</p>
<p>{{g}}</p>
<p>{{  config  }}</p> # 拿到flask的配置信息

2.注释

{# 注释不会再页面中渲染  #}
    
3.url_for

在模板中使用url_for
<p>{{ url_for('user',uid=3) }}</p>
user 视图必须存在要不然会报错
/user/3 # 动态路由
# 视图:
@app.route(rule='/user/<int:uid>')
def user(uid):
    pass
    
# 如果视图中不存在/<int:uid>接收参数
<p>{{ url_for('user',uid=3) }}</p>
/user?uid=3 # url就会变为带有请求参数
# 视图:
@app.route(rule='/user')
def user():
    pass    


4.流程控制语法 控制代码块 # 与python一模一样
if / elif / else / endif  判断
for / else / endfor 循环
4.1循环
lis = [1,2,3]
lis = []
{% for i in lis %}
    # loop.index 获取循环的下标 从1开始
    # loop.first 如果是第一次迭代 为True
    # loop.last 最后一次迭代为True
    # loop.cycle('男-为True',"女-为False") 在数据库中假设1为nan 0为女 布尔就是True False loop.cycle就是处理 True False的
    <p>{{ loop.index }}  {{ i }}</p>
{% else %}
    <p>没有哦</p> # lis为空那么就会直行当前else语法(与python的一样)
{% endfor %}
    
4.2判断
start = False
{% if start %}
    <p>1234556</p> # ture 显示
{% else %}
    <p>666</p> # false 显示
{% endif %}

模板过滤器语法内置

变量名 | 过滤器 | .... # 过滤器左边的内容作为过滤器的第一个参数

1.safe
lis = "<h1>66666</h1>"
<p>{{ lis|safe }}</p> # safe 告诉flask 不用转义直接使用 默认是转义的防止用户输入js代码在网站中执行

2.reverse 数据翻转 (列表元祖复杂类型会为生成器需要循环)
lis ='python'
<p>{{ lis | title }}</p>

3.title 首字母大写
lis ='python'
<p>{{ lis | title }}</p>

4.upper 大写 lower 小写
lis ='python'
<p>{{ lis | upper }}</p>

5.d or default d默认值
如果lis不存在 那么在模板中显示的值就是默认值中的数
<p>{{ lis | default('2131323') }}</p>

6.字符串截断
lis ='asdadsawqxzzcz'
<p>{{ lis | truncate(5,'默认参数False 如果传入True那么就是强制截断字符中的空格也会算上') }}</p>  将截断内容在html中以..显示 as...(最低截断不能小于3)

7.format 格式化输入
<p>{{ '%s=%s'|format('name','wkx') }}</p> # name=wkx 

8.striptags 将选中的数据中的html标签全部去掉 # 不能使用大于号小于号
<p>{{ '<h1>sadassda</h1>'|striptags }}</p> # sadassda

9.first 获取列表的第一个元素 set 元祖 列表 字典(只能获取第一个kv的k) 字符串 
lis =[1,2,3,4,5,6,7,8,9]
<p>{{ lis | first }}</p> # 1

10.last 获取列表的最后一个元素 set 元祖 列表 字典(只能获取第一个kv的k) 字符串 
lis =[1,2,3,4,5,6,7,8,9]
<p>{{ lis | last }}</p> # 9

11.length/count 获取元素长度 set 元祖 列表 字典(只能获取第一个kv的k) 字符串 
lis =[1,2,3,4,5,6,7,8,9]
<p>{{ lis | length }}</p> # 9

12. sum 获取序列类型的求和 set 元祖 list
<p>{{ lis | sum }}</p>
lis =[1,2,3,4,5,6,7,8,9]
<p>{{ lis | sum }}</p> # 45

13.sort 获取列表的排序 支持字典按照key set list 元祖
lis =[1,2,3,4,5,6,7,8,9]
<p>{{ lis | sort }}</p> sort(reverse=true) 倒叙 
sort(attribute="age") # 按照字典的key age 进行排序 

模板过滤器语法-过滤块

语法:
{% filter 过滤方法 %} # 会将当前范围内的全部根据过滤器进行转变
    <p>{{ 'a,baaa' }}</p> # 变量
    <p>{{ 'a,baaa' }}</p>
    <p>{{ 'a,baaa' }}</p>
    <p>{{ 'a,baaa' }}</p>
{% endfilter %}

例如:
{% filter upper %}
    <p>{{ 'a,baaa' }}</p> # A,BAAA 全部变为大写字母
    <p>{{ 'a,baaa' }}</p>
    <p>{{ 'a,baaa' }}</p>
    <p>{{ 'a,baaa' }}</p>
{% endfilter %}

自定义过滤器

过滤器本质就是函数,当模板内置的过滤器不能满足当前的要求,就可以自定义过滤器
1.通过flask引用程序对象app.add_template_filter进行注册
2.通过装饰器进行注册

# 注意:不能与内置的过滤器进行重名,会将原有的过滤器进行覆盖
# 也就是创建一个函数注册到过滤器中


1.app.add_template_filter('注册过滤器的函数','过滤器的名字')

例如:
def do_fixed(data): # 注册过滤器的函数
    '''
    :param data:  接收模板传入的参数
    :return:
    '''
    return 1 # 返回为1


app.add_template_filter(do_fixed, 'fixed')  # 将过滤器函数注册


在模板中使用
<p>{{ 'llll'|fixed }}</p> # 显示 1


2.批量导入自定义过滤器(适用于注册形式)
filter.py # 自定义过滤器文件
def do_xx(data):
    pass
def do_xx2(data):
    pass

FILTERS = {
    'xx':do_xx,
    'xx2':do_xx2
}

from  filter  import FILTERS # 在app程序中导入
# 批量注册自定义过滤器
for key,val in FILTERS:
    app.add_template_filter(val, key)
    
    
3.通装饰器进行注册
@app.template_filter('过滤器名称')
def 函数(data):
    '''
    :param data:  接收模板传入的参数
    :return:
    '''
    pass

例如:
@app.template_filter('fixed')
def do_fixed(data):
    '''

    :param data:  接收模板传入的参数
    :return:
    '''
    return 1

模板继承语法

与vue的导入组件导入是类似的
vue:将公共的组件创建为 xx.vue文件 如果使用到了公共组件就将它进行导入,注册 使用

模板继承:
做一个公共使用的html页面,页面中留出一些公用的位置,这也位置的内容需要根据业务的不同变化

在模板中遇到的情况:
多个模板使用同一个底和顶
多个模板具有相同的代码内容,但是内容不同
多个模板具有相同的html代码块内容,侧边栏

目的: 重复的使用模板中的公共内容,一般情况主要使用网站的顶部菜单,侧边栏,广告,这些内容可以定义到父模板中,子模板直接继承,不需要重复书写


# 使用的是block 相当于在父模板中挖了一个坑,当子模板继承父模板是,可以对坑的位置填补需要的内容

# 注意:
    1.子模板使用extends声明在我为那个父模板填坑(每个子模板只能为继承一个父模板)
    2.父模板定义的区域,会在子模板中重新定义,在字母本中调用父模板的内容可以使用super()调用父模板声明的区域内容
    
    
1. base.html  // 父模板文件

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
       {{ title }}
    </title>
</head>
<body>
<h1>京东商城</h1>
<div>
     // 在父模板挖的一个坑
    {% block content %}
        <p>父模板的默认内容</p>  如果子模板没有填坑那么就会使用父模板默认的
    {% endblock content %}
</div>
<h1>我是底部内容</h1>
</body>
</html>
     
     
2.index.html // 子模板文件
// 1.需要导入父模板,知道子模板继承了那个父模板(基于template 寻找的父模板文件 注意路径) 存在嵌套文件: 嵌套文件夹/父模板.html

{% extends  'base.html' %}

// 2.对父模板的坑进行填写(如果没有填写那么就会使用父模板的默认内容内容) 

{% block content %}
    <p>我是首页</p>
{% endblock  %}


3.视图
@app.route(rule='/')
def index():
    title = '站点首页'
    html = render_template("index.html", **locals())
    return html
 
// 4.使用方法  *****

父模板:
     {% block 坑的名字 %}
       // 挖个坑,让子模板继承后进行填写
        默认内容 // 子不填 我用自己的
    {% endblock 坑的名字 %}
    
子模板 :
  {% extends  '继承父模本文件.html' %}
  
       {% block 坑的名字 %}
       // 我为父模板填坑(如果我不填,那么就会用父模板的内容)
    {% endblock  %}
    
//5.使用是的注意情况

1.不支持多继承,在同一个模板中只能使用一个extends
2.在子模板第一行使用extends 告知继承父模板
3.不能在模板中同时定义多个相同名称 block 会进行覆盖
4.当使用多个block 请为结束enblock标记相同与block名称 方便阅读

Flask-CSRF攻击防范

如果使用前后端不分离的情况防止csrf可以基于
pip install flask_wtf # 模块进行防止

1.将app注册到wtf模块中
from flask import Flask
from flask_wtf import CSRFProtect  # 2.导入防范机制类

app = Flask(__name__, template_folder='templates')
app.config['SECRET_KEY'] = '防止CSRF攻击(仅限于前后端不分离)' # 1.添加配置
# 原理:在每次提交post请求的表单时,都会验证请求投头中是否存在csrf的token
csrf = CSRFProtect()  # 3.实例化csrf
csrf.init_app(app)  # 4.将app注册


2.在html表单中使用
会将csrf携带到视图中,如果存在验证通过,如果失败验证不通过
<form action="/send_money" method="post">
    {#    使用令牌   #}
    <input type='hidden' name="csrf_token" value="{{ csrf_token() }}">
    账户: <input type="text" name="user">
    密码: <input type="text" name="pwd">
    <input type="submit" value="转账">
</form>
        
        
中文文档:
http://docs.jinkan.org/docs/flask-wtf/quickstart.html

构建flask工厂方式配置方式

from flask import Flask


class DefaultConfig:  # 生产模式 所需要的全部参数
    SECRET_KEY = ''


class DevelopmentConfig(DefaultConfig):  # 调试模式下的参数
    '''继承了生成模式的类'''
    DEBUG = True


def create_flask_app(classobj):
    # 启动配置
    app = Flask(
        __name__,
        static_url_path='/python',
        static_folder='static_folder'
    ) # 启动对象
    app.config.from_object(classobj) # 类加载配置
    app.config.from_pyfile('view/apps.py') # 文件加载配置(将重要的参数放到文件中,会覆盖将类中的重要参数进行覆盖)

    return app # 返回flask框架的app对象


app = create_flask_app(DevelopmentConfig) # 将调试模式下的类存放

Flaks-蓝图使用

蓝图等价于django中的子应用程序(模块化思想)
特点:
    每一个 app 都可以有多个蓝图
    可以给每一个蓝图注册一个路由的前缀
    蓝图可以有自己的模板静态等等自己的文件
    需要在flask进行注册进去(必须注册,和django一样)
    
from flask import Flask, Blueprint

app = Flask(__name__)
#  Blueprint('别名',__name__)
user_bp = Blueprint('user', __name__) # 1.创建蓝图对象


@user_bp.route('/login') # 2.使用蓝图对象注册视图 装饰器方式
def login():
    return 'login'


# 3.将蓝图对象注册到app下 register_blueprint('蓝图对象','url前缀')
app.register_blueprint(blueprint=user_bp, url_prefix='/api')
if __name__ == '__main__':
    app.run(debug=True)

# 访问地址 =    register_blueprint 注册蓝图的url前缀 +  @user_bp.route 注册的url 
# /api/login

包形式蓝图

包中__init__.py文件

init文件:
from flask import Blueprint  # 蓝图对象
from . import views

# Blueprint('别名',__name__)
# 相当于声明一个子app app = Flask(__name__)

users_bp = Blueprint('user_api', __name__)

# 使用add_url_rule进行注册url 视图 绑定视图到路由中
users_bp.add_url_rule(rule='/login', view_func=views.login)
users_bp.add_url_rule(rule='/user_get', view_func=views.user_get)

包中的views.py文件

def user_get():
    return '登录'


def login():
    return '注册'

项目入口文件manage.py

from flask import Flask
from user import users_bp # 导入

app = Flask(__name__)
app.register_blueprint(users_bp, url_prefix='/api') # 注册
if __name__ == '__main__':
    app.run(debug=True)

蓝图运行原理

1.蓝图实际作用就是,充当 当前蓝图目录下所有视图和url路由地址绑定关系的临时容器。

2.在视图函数被蓝图对象.add_url_rule注册时,本质就是将视图与url地址的映射关系添加到蓝图的子路由列表中deferred_functions

3.蓝图本身没有路由机制,当蓝图的视图调用add_url_rule注册时,他是在蓝图对象内部中的deferred_functions(子路由列表)中添加了一个路由项(绑定关系)

4.当进行app.register_blueprint注册蓝图时,app实例对象就会将蓝图的deferred_functions列表中进行循环,将每一个路由项取出,并且app应用实例对象作为参数执行路由项对应lambda匿名函数,匿名函数直行后就会调用add_url_rule方法,将蓝图下的子路由列表暂存的路由添加到app的url_mao总路由列表中,所有用户可以flask中访问蓝图中的视图

# add_url_rule : 将全部的路由url与函数绑定的关系执行流程
'''
在内部调用了实例化蓝图对象中的add_url_rule对象
1.
接收参数(比较重要)
    rule: url地址
    view_func: 视图函数名称
进行了:
self.record( # 调用了内部的record方法 传入了一个lambda的匿名函数
            lambda s: s.add_url_rule(
                rule, url地址
                endpoint, 别名
                view_func, 视图名称
                provide_automatic_options=provide_automatic_options,
                **options, 其他参数
            )
        )
2.
def record(func...): 这个方法内部执行了将 匿名函数添加到deferred_functions列表中
    self.deferred_functions.append(func)
3.
deferred_functions列表参数是蓝图类实例化init初始化的一个参数

'''


# register_blueprint 执行
'''
register_blueprint 在内部调用了第一个参数的(蓝图对象内的方法)方法
1.
blueprint.register(self, options) 将flask对象本身与剩余参数
2.
执行当前蓝图下的这个方法
def register(self, app: "Flask", options: dict)
3.
register方法中
基于app对象进行封装获取state对象
state = self.make_setup_state(app, options, first_bp_registration)
执行了make_setup_state传入了flaskapp与与剩余的其他参数
4.
执行了注册了访问的静态路由方式
state.add_url_rule(
                f"{self.static_url_path}/<path:filename>",
                view_func=self.send_static_file,
                endpoint="static",
            )
5.
将deferred_functions列表中进行循环(全部都是蓝图注册的路由和函数名称,匿名函数lambda)
将state传入 传入
for deferred in self.deferred_functions:
      deferred(state)    

6.在state对象中就是将类型了实例化
BlueprintSetupState(self, app, options, first_registration)  就是将对象纯初始化     

7. 
deferred(state) 
lambda s: s.add_url_rule(
            rule, url地址
            endpoint, 别名
            view_func, 视图名称
            provide_automatic_options=provide_automatic_options,
            **options, 其他参数
        )  
那么这个匿名函数的s也就是state
state.add_url_rule(而state本身就是flask的app对象)
BlueprintSetupState类中的
self.app = app
self.app.add_url_rule(
            rule,
            f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."),
            view_func,
            defaults=defaults,
            **options,
        )
flask.add_url_rule(将蓝图中的路由进行注册)       
        
'''

url_for

form flask import url_for
url_for('蓝图名称.视图函数名称')

Flask-补充

1.提供下载功能

import os
import pathlib
import mimetypes
from flask import Flask,Response,make_response,send_from_directory
base_path = pathlib.Path(__file__).parent
app = Flask(__name__)
'''
下载的时候要注意的问题:
    1.设置响应请求头的类型 : Content-Type 平常都是json格式,文件的话是其他的格式
    2.告诉浏览器当前下载的作为浏览器的附件下载属性: Content-Disposition https://cloud.tencent.com/developer/section/1189916

'''

@app.route('/dow')
def dow():
    path = os.path.join(base_path,'下载文件.txt')
    file = open(path,mode='rb')
    # response = make_response(file)
    # response.default_mimetype = 'text/html'
    # response['Content-Disposition']='attachment; filename="filename.txt"'
    '''
    path: 下载的文的路径 相当于 open(path,mode='rb') Content-Type: text/plain; charset=utf-8
    filename:下载文件的名称和后缀 相当于请求头中的 filename="filename.txt
    directory: 当前下载文件的父级路径
    as_attachment : False则浏览器返回文件预览 如果该文件可以被浏览器渲染  response['Content-Disposition']='attachment; 
    '''
    return send_from_directory(path=path,filename='下载文件.txt',as_attachment=False,directory=base_path)

@app.route('/dow2')
def uploaded_file():
    path = os.path.join(base_path,'下载文件.txt')
    f = open(path, "rb")
    res = Response(f.readlines())
    mime_type = mimetypes.guess_type(path)[0]
    res.headers['Content-Type'] = mime_type
    res.headers['Content-Disposition'] = "attachment; filename=filename.txt"
    return res



if __name__ == '__main__':
    app.run()
    
    
'''
资料案例
https://blog.csdn.net/li627528647/article/details/77544136
https://blog.csdn.net/qq_44198436/article/details/106922355
https://blog.csdn.net/WJ844908240/article/details/102517072
''' 

Flase扩展包

Flask邮件模块

1.安装:pip install flask-mail


基本使用

import os

from flask import Flask
from flask_mail import Mail, Message

''' 
qq 邮箱的授权码 hvijhzswucrjbeff
一般在邮箱中的设置中账户
POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
开启 pop3的服务 会给一个授权码,就可以代替 发送邮件服务
'''
app = Flask(__name__)
app.config["MAIL_SERVER"] = "smtp.qq.com"  # 邮箱的服务器的主机名和ip地址
app.config["MAIL_PORT"] = 465  # 设置邮箱端口为465
app.config["MAIL_USE_SSL"] = True  # QQ邮箱需要开启SSL启动安全接层  谷歌需要开启MAIL_USE_TLS  TLS 启动传输层协议
app.config["MAIL_USERNAME"] = "565151759@qq.com"  # 发送者邮箱名
app.config["MAIL_PASSWORD"] = "hvijhzswucrjbeff"  # 开启POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务返回的授权码 代替密码 使用这个邮箱代发邮件

mail = Mail()
mail.init_app(app)  # 初始化当前的app对象到mail中去

# 发送的 文件
path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '测试001.xlsx')


@app.route("/email")
def emailDemo():
    """
    Message发送邮件信息, sender为发送者邮箱:读取的配置文件的中使用发送者邮箱, recipients为接受者邮箱:列表形式
    """
    message = Message("测试邮件标题1000", sender=app.config["MAIL_USERNAME"], recipients=["565151759@qq.com"])
    print(message)
    message.body = "测试邮件的内容1000"  # 设置内容
    message.html = '<h1>123</h1>'  # 设置大的标题

    # 添加附件
    # 打开文件
    with app.open_resource(path) as doc:
        # attach("文件名(名称需要转义)", "类型", 读取文件)
        message.attach("001", 'application/octet-stream', doc.read())

    # 调用send_email函数防止出现上下文问题
    send_email(message)
    return "发送成功"


# 防止上下文的问题使用 app.app_context()
def send_email(message):
    with app.app_context():
        # mail.send 调用这个方法 将message邮箱内容 对象进行发送
        mail.send(message)


if __name__ == "__main__":
    app.run(debug=True)

异步方式邮件(开启线程)

from threading import Thread

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
app.config["MAIL_SERVER"] = "smtp.qq.com"  # 邮箱的服务器的主机名和ip地址
app.config["MAIL_PORT"] = 465  # 设置邮箱端口为465
app.config["MAIL_USE_SSL"] = True  # QQ邮箱需要开启SSL启动安全接层  谷歌需要开启MAIL_USE_TLS  TLS 启动传输层协议
app.config["MAIL_USERNAME"] = "565151759@qq.com"  # 发送者邮箱名
app.config["MAIL_PASSWORD"] = "hvijhzswucrjbeff"  # 开启POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务返回的授权码 代替密码 使用这个邮箱代发邮件

mail = Mail()
mail.init_app(app)


# 执行命名函数,需要用线程进行调用
def sand_async_email(app, msg):
    with app.app_context():
        mail.send(msg)


def sand_email(app):
    msg = Message('这是一个异步的发邮件方式', sender=app.config['MAIL_USERNAME'], recipients=['565151759@qq.com'])
    msg.body = '你是个傻逼吗'
    # 调用线程执行的方式
    # 传入函数,传入参数 app 对象和 msg发送邮件内容对向
    thr = Thread(target=sand_async_email, args=[app, msg])
    thr.start()  # 执行
    return thr


# 使用线程去调用邮箱执行命令,速度更快
print(sand_email(app))

Flask_Migrate迁移

使用版本 2.7.0
1.导入包
from flask import Flask # 框架实例
from flask_sqlalchemy import SQLAlchemy # 数据库
from flask_migrate import Migrate # 迁移包

# 实例化flask 对象
app = Flask(__name__) # 实例化对象
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' # 配置数据库地址 使用dblist
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #设置数据创建跟踪
db = SQLAlchemy(app) # 将实例化的app对象传入sqlalchemy中
migrate = Migrate(app, db) # 在将app对象和db对象到迁移包中



class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))


1. 在命令行创建flask 的环境变量
    set FLASK_APP = 执行的py文件
    
2. 在命令行使用
    flask db init  # 创建迁移的文件夹
    
3. flask db migrate
    创建一个迁移的脚本

4. flask db upgrade
    将创建的数据类迁移到数据库中

表结构分开

初始化文件 __init__.py
from flask import Flask # 框架实例
from flask_sqlalchemy import SQLAlchemy # 数据库
from flask_migrate import Migrate # 迁移包

# 实例化flask 对象
app = Flask(__name__) # 实例化对象
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' # 配置数据库地址 使用dblist
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True #设置数据创建跟踪
db = SQLAlchemy(app) # 将实例化的app对象传入sqlalchemy中
migrate = Migrate(app, db) # 在将app对象和db对象到迁移包中



model.py 文件(数据库的类文件)
from app import db
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    age = db.Column(db.Integer)

    
main.py 启动文件
from app import app
from app.models import User # 需要将创建的表模型添加启动文件中

if __name__ == '__main__':
    app.run()

# 在进行执行
创建新的表类,需要启动文件中进行导入。在进行加载和迁移(适用表类与实例对象分开的状态)
1.先设置环境变量 
    set FLASK_APP = 启动文件.py
flask db init # 创建文件(只用创建一次就可以)
flask db migrate # 先加载 
flask db upgrade # 在迁移

Flask-Cors

# 跨域
pip install flask-cors
from flask_cors import CORS
CORS(app, resources=r'/*')	# 注册CORS, "/*" 允许访问所有api

Flask-session

flask 框架中的session,默认以cookie的方式将session的数据分散保存到客户端中,并非真正意义上的session数据保存,使用flask-session,将session数据指定保存到服务端中(缓存或者数据库中)
可以指定存储的媒介:redis / mongodb / mysql
官方文档:
    https://flask-session.readthedocs.io/en/latest/
安装
pip install Flask-Session

保存到数据库mysql

from flask import Flask
from flask_sqlalchemy import SQLAlchemy # 1.导入数据库对象
from flask_session import Session as SessionStore  # 1.导入flask_session实例对象
from flask import session # 使用的还是flask中的session对象


app = Flask(__name__)
db = SQLAlchemy()  # 2.实例化数据库对象
session_store = SessionStore()  # 2.实例化flask_session类
# 3.设置session的配置(使用的mysql作为存储session的媒介的配置如下)
app.config.update({
    # 指定数据库配置项
    'SQLALCHEMY_DATABASE_URI': 'mysql://root:123456@127.0.0.1:3306/flask?charset=utf8mb4',  # SQLAlchemy数据库链接地址
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,  # 是否追踪对象
    'SQLALCHEMY_ECHO': False,  # 是否显示sql语句
    'DEBUG': True,
    'SECRET_KEY': '13213213ASDAQWQWEASD',  # 设置session秘钥

    # 把session通保存到mysql中以sqlalchemy为媒介进行保存
    "SESSION_TYPE": 'sqlalchemy',  # 会话类型 redis / sqlalchemy(mysql) 等等方式
    'SESSION_SQLALCHEMY': db,  # session保存的数据库链接对象(mysql类型的数据库)
    'SESSION_SQLALCHEMY_TABLE': 'sessions',  # session保存的表名默认是session
    'SESSION_PERMANENT': True,  # 设置会话期,True关闭浏览器后失效
    'SESSION_USE_SIGNER': True,  # 是否对发送到浏览器上的session的cookie值进行添加签名,防止修改
    'SESSION_KEY_PREFIX': 'session:'  # 对存储的session添加标识字符 前缀

})
# 4.数据库初始化
db.init_app(app)
# 4.session类初始化
session_store.init_app(app)


@app.route('/')
def index():
    # 1.存储session
    session['user_name'] = 'wkx'
    session['age'] = 18
    # 2.获取session
    print(session.get('age'))
    print(session.get('user_name'))
    # 3.删除session
    session.pop('age')
    session.pop('user_name')
    return 'ok'

if __name__ == '__main__':
    # 5.如果使用sqlalchemy作为数据库配置需要进行使用db.create进行数据库创建(在数据库中就会出现一个SESSION_SQLALCHEMY_TABLE设置的表)
     with app.app_context():
         db.create_all()  # 创建数据库
    app.run()
    
1.保存到数据库中后,需要创建数据库
2.删除后,记录还是存储在数据库中
3.如果量大的情况下,不建议使用存储到数据库,速度太慢

保存到redis中

from flask import Flask
from flask_session import Session as SessionStore  # 1.导入flask_session
from flask_redis import FlaskRedis  # 导入flask_redis对象
from flask import session
app = Flask(__name__)
session_redis = FlaskRedis(config_prefix='REDIS_SESSION')  # 实例化redis对象
session_store = SessionStore()  # 实例化flask_session类
app.config.update({
    # 设置redis的链接url
    'REDIS_SESSION_URL': "redis://127.0.0.1:6379/0",  # config_prefix='REDIS_SESSION'
    'SECRET_KEY': '13213213ASDAQWQWEASD',  # 设置session秘钥 必须设置
    # 保存到redis中
    "SESSION_TYPE": 'redis',  # 设置为redis
    'SESSION_PERMANENT': True,  # 设置会话期,True关闭浏览器后失效
    'SESSION_USE_SIGNER': True,  # 是否对发送到浏览器上的session的cookie值进行添加签名,防止修改
    'SESSION_KEY_PREFIX': 'session:',  # 对存储的session添加标识字符 前缀
    'SESSION_REDIS': session_redis,  # 链接对象(实例化的链接对象)

})

# 2.初始化redis
# 2.初始化session
session_redis.init_app(app)
session_store.init_app(app)


@app.route('/')
def index():
    # 1.设置session
    session['user'] = 'wkx'
    # 2.查询session
    print(session.get('user'))
    # 3.删除session
    session.pop('user')

    return 'ok'


if __name__ == '__main__':
    app.run()

Flask-redis

安装:为什么:这样使flask app直接接管redis比较方便 将配置卸载app.config中就可以(底层还是pymysql)
pip install flask-redis
https://pypi.org/project/flask-redis/
from flask import Flask
from flask_session import Session as SessionStore  # 1.导入flask_session
from flask_redis import FlaskRedis  # 导入flask_redis对象

app = Flask(__name__)

app.config.update({
    # 'REDIS_URL':redis://:密码@ip:端口/几号库
    # 如果没有密码redis://127.0.0.1:6379/0
    'REDIS_SESSION_URL': "redis://127.0.0.1:6379/0",  # config_prefix='SESSION'
    'REDIS_USER_URL': "redis://127.0.0.1:6379/1",  # config_prefix='USER'
    'REDIS_ORDER_URL': "redis://127.0.0.1:6379/2",  # config_prefix='ORDER'
})
# 1.redis因为有16个库的,可以设置多个库进行操作
# 默认链接不同的库中,可以设置redis 的前缀作config_prefix前缀
# 配置信息需要根据设置的前缀取设置链接的库
session_redis = FlaskRedis(config_prefix='REDIS_SESSION')
user_redis = FlaskRedis(config_prefix='REDIS_USER')
order_redis = FlaskRedis(config_prefix='REDIS_ORDER')
# 2.初始化redis
session_redis.init_app(app)
user_redis.init_app(app)
order_redis.init_app(app)
session_store = SessionStore()  # 实例化flask_session类


@app.route('/')
def index():
    # 直接使用实例对象进行操作即可  select 切换库
    # 操作和redis一致 还是调用了redis.Redis对象
    session_redis.setnx('age', 100)
    user_redis.setnx('user', 100)
    order_redis.setnx('order_id', 100)
    return 'ok'


if __name__ == '__main__':
    app.run()