推荐
关注
TOP
Message

Flask基础

1. flask框架

核心:Werkzerug+Jinja2

轻——>只提供核心

2.0.1

2. 框架的对比

问题:

  1. django与flask谁好?
    没有最好 只有最合适
  2. 对比两个框架?
    轻 重

框架选择上:

自由,灵活,高度定制,flask

封装好的,单独开发 快速开发 django

3. 工程搭建

  1. django
    django-admin startproject xxx
    python manage.py startproject users
    python manage.py runserver
  2. Flask
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'hello,world'

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

直接python+文件名

或者直接用pycharm运行

  1. 初始化参数
    • import name
    • static_url_path
    • static_folder
    • template_folder

4. 加载config 配置 三种方式

  1. 直接用类名加载
from flask import Flask

app = Flask(import_name=__name__)

class DefaultConfig(object):
    '''默认配置'''
    SECRET_KEY = 'ABCDEFG'

app.config.from_object(DefaultConfig)

@app.route('/')
def index():
    return app.config['SECRET_KEY']

if __name__ == '__main__':
    app.run()
  1. 在文件中加载
from flask import Flask

app = Flask(import_name=__name__)

app.config.from_pyfile(filename="setting.py")

@app.route('/')
def index():
    print(app.config['SECRET_KEY'])
    return 'hello,world'

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

优点:独立文件,保护敏感数据

缺点:不能继承,文件路径固定不灵活

应用场景:不考虑灵活性,仅保护敏感数据

  1. 在环境变量中加载
from flask import Flask

app = Flask(import_name=__name__)

# 环境变量 需要在系统中
'''
export PROJECT_SETTING=setting.py
'''
# 变量只有单独终端可以拿到,有生命周期的 是局部变量
app.config.from_envvar('PROJECT_SETTING')
# app.config.from_envvar('PROJECT_SETTINGJH',silent=True)
# silent的作用:如果PROJECT_SETING不存在 flask让你自己决定作用 False 不存在 直接抛出异常
# silent = True 安静处理 如果没有也不会报错

@app.route('/')
def index():
    print(app.config['SECRET_KEY'])
    return 'hello,world'

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

优点: 独立文件,保护敏感数据,文件路径固定灵活

缺点: 不方便,要设置环境变量

  1. pycharm 中加载环境变量
  2. config加载方式一般结合使用
from flask import Flask

class DefaultConfig(object):
    '''默认配置'''
    SECRET_KEY='DEFAULT VALUE'

class DevelopmentConfig(DefaultConfig):
    DEBUG = True

def create_flask_app(config):
    '''构建flask对象的工厂函数'''
    app = Flask(import_name=__name__)
    # 设置
    app.config.from_object(config)

    app.config.from_envvar(variable_name='PROJECT_SETTING',silent=True)
    return app

# app = create_flask_app(DefaultConfig)
app = create_flask_app(DevelopmentConfig)

@app.route('/')
def index():
    print(app.config['SECRET_KEY'])
    return 'hello,world'

if __name__ == '__main__':
    # 提供的是调试服务器
    app.run(host='127.0.0.1',port=8888,)

5. 运行参数

  1. app.run(host='0.0.0.1',port='8888',debug=True)
  2. 再系统中export FLASK_APP='工程名'export FLASK_ENV=developmentflask run -h '127.0.01' -p 3399
  3. 在pycharm中

6. 路由

1.查询路由

  1. 在完成工程配置后 在终端设置完环境变量结束
    flask routes
  2. 直接在项目中打印
print(flask.url_map)
  1. 构造全部路由信息的接口
import json
from flask import Flask

class DefaultConfig(object):
    '''默认配置'''
    SECRET_KEY='DEFAULT VALUE'

# class DevelopmentConfig(DefaultConfig):
#     DEBUG = True

def create_flask_app(config):
    '''构建flask对象的工厂函数'''
    app = Flask(import_name=__name__)
    # 设置
    app.config.from_object(config)

    app.config.from_envvar(variable_name='PROJECT_SETTING',silent=True)
    return app

app = create_flask_app(DefaultConfig)
# app = create_flask_app(DevelopmentConfig)

'''
@app.route('/')
def index():
    print(app.config['SECRET_KEY'])
    return 'hello,world'

# print(app.url_map)
# todo: 需求:需要便利 url_map 取出特定信息 在一个特定借口返回
for rule in app.url_map.iter_rules():
    # 返回了一个列表
    print('name={} path={}'.format(rule.endpoint, rule.rule))
'''

@app.route('/')
def route_map():
    '''
    主视图,返回所有的视图网址
    :return:
    '''
    rules_iterator = app.url_map.iter_rules()
    return json.dumps({rule.endpoint: rule.rule for rule in rules_iterator})

# if __name__ == '__main__':
#     # 提供的是调试服务器
#     app.run(host='127.0.0.1',port=8888,)

主要有两种方式

  1. flask routes
  2. app.url_map

2. 请求方式

GET

OPTIONS(自带) -> 简化版的get请求用于询问 服务器接口信息的

比如接口允许的请求方式,允许的请求源头域名

  • CORS 跨域 django-cors
  • www.meiduo.site -> api.meiduo.site
    • options api.meiduo.site/use/1
      返回response -> allow-orgin'www.meiduo.site'
    • GET api.meiduo.site/users/1

HEAD(自带) 简化版的get请求

  • 只返回get请求处理时的相应请求 不反悔响应体

自定 post put delete patch

利用method参数 自定义接口的请求方式

405 methods not allowed

from flask import Flask

app=Flask(import_name=__name__)

@app.route('/', methods=['POST'])
def index():
    return 'hello,world'

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

7. 蓝图

1. 创建蓝图过程

from flask import Flask, Blueprint

app = Flask(import_name=__name__)

# 创建蓝图
user_bp = Blueprint('user', __name__)

# 定义蓝图路由
@user_bp.route('/profiles')
def get_profiles():
    return 'user profiles'

# 注册蓝图
app.register_blueprint(user_bp, url_prefix='/user')

# 导包 分解解耦 将路由当成一个app
from goods import goods_bp
app.register_blueprint(goods_bp)

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

在其他目录下(python包)创建views视图

init包下写如下代码

from flask import Blueprint

goods_bp = Blueprint('goods', __name__)

from . import views

在views.py下如下代码

from . import goods_bp

@goods_bp.route('/goods')
def get_goods():
    return 'get_goods'

2. 蓝图内部文件

admin=Blueprint('admin',__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')

可通过/admin/static_admin/filename 来访问也可以通过static_url_path来改变访问路径

admin=Blueprint('admin',__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')

3. 蓝图内部模板文件

admin=Blueprint('admin',__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')

8. 请求与相应

1. 处理请求

请求报文
GET /path?a=1 http/1.1
content-Type: application/Json
...

body -> file form json xml

def func(request,....):
    request.

flask 不同于django 定义url 不用正则表达式 而是采用转换器语法

比如 /user/123

flask是

@app.route('/users/<user_id>')
def user_info(user_id):
    print(type(user_id))
    return 'hello user {}'.format(user_id)

通过某种规则 来匹配数据

@app.route('/users/<int:user_id>')
def user_info(user_id):
    print(type(user_id))
    return 'hello user {}'.format(user_id)
from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)

@app.route('/user/<int:user_id>')
def get_users_data(user_id):
    print(type(user_id))
    return 'get users {}'.format(user_id)

class MobileConverter(BaseConverter):
    regex = r'1[3-9]\d{9}'

app.url_map.converters['mobile'] = MobileConverter

@app.route('/sms_codes/<mobile:mob_num>')
def send_sms_code(mob_num):
    print(type(mob_num))
    return 'send sms code to {}'.format(mob_num)

https://127.0.0.1:5000/articles?channel_id=4353

# /articles?channel_id=4353
@app.route('/articles')
def get_articles():
    channel_id = request.args.get('channel_id')
    return 'get articles is {}'.format(channel_id)
# /articles?channel_id=4353
@app.route('/upload',method='post')
def upload_file():
    f=request.files['pic']
    f.save('./demo.png')
    return 'ok'

2. 处理响应

HTTP/1.1 200 ok
Content-Type: application/json
...
body

返回模板 render_template

from flask import Flask,request,render_template
from werkzeug.routing import BaseConverter

app = Flask(__name__)

# @app.route('/')
# def home():
#     mint = 123
#     mstr = 'hello,world'
#     return render_template('index.html', my_str=mstr, my_int=mint)
@app.route('/')
def home():

    data = dict(
        my_str='hello,world',
        my_int=123
    )

    return render_template('index.html', **data)

3. 重定向

和django一样 redirect

4. 返回Json

import json

json.dumps({})
  • return json.dumps()
    • 仅仅把数据转换成格式返回
    • 响应头不做任何操作
  • return jsonify
    • 转换成json格式字符串
    • 设置了响应头content-Type: application/json

5. 自定义状态码和响应头

元组方式

一个函数返回多值

def func():
    return 1,2,3

ret= func()
r1,r2,r3=func()

元组方式 必须是(response,status,headers)

@app.route('/demo4')
def demo4():
    # return '状态码为 666', 666
    # return '状态码为 666', 666, {'itcast': 'python'}
    return '状态码为 666', 666, [('itcast','python')]

make_response 方式


@app.route('/demo5')
def demo5():
    resp = make_response('make response 测试')
    resp.headers['Itcast'] = 'Python'
    resp.status='404 not found'
    return resp
  • 设置cookie
@app.route('/set_cookie')
def set_cookie():
    resp = make_response('set cookie')
    resp.set_cookie('Itcast','Python',max_age=3600)
    return resp

后面+ max_age=3600 是设置时间 不加默认下次关闭即删除cookie

  • 读取cookie
@app.route('/get_cookie')
def get_cookie():
    resp = request.cookies.get('Itcast')
    return resp
  • 删除cookie
@app.route('/delete_cookie')
def delete_cookie():
    resp=make_response('delete cookie ok')
    resp.delete_cookie('Itcast')
    return resp

7. session

  • 设置session
class DefaultConfig(object):
    SECRET_KEY = 'kasjdaskldjas'

app.config.from_object(DefaultConfig)

@app.route('/set_session')
def set_session():
    session['Itcast'] = 'Python'
    return 'set session is ok'
  • 获取session
@app.route('/get_session')
def get_session():
    Itcast = session.get('Itcast')
    return 'get Session Itcast is {}'.format(Itcast)
  • 那么flask将session 存到了哪里去了
    flask------> 浏览器session 数字签名

9. 请求钩子与上下文

和django里面的中间件相同

  1. 异常处理
# /articles?channel_id=4353
@app.route('/articles')
def get_articles():
    channel_id = request.args.get('channel_id')
    if channel_id is None:
        abort(400) # 400 是代表bad request
    return 'get articles is {}'.format(channel_id)

用 abort 来传入状态码

  1. 捕获错误
    展示给用户的友好提示
@app.errorhandler(500)
def inter_server_error(e):
    return '服务器搬家了'

也可以根据特定异常回

@app.errorhandler(ZeroDivisionError)
def zerodivison(e):
    print(e)
    return '除数不能除以0'
  1. 请求钩子
    启动中间件/中间层的作用
    middleware1 -> class middleware1
    def pre_process()
    def after_process()
    middleware2
    middleware3
    请求的处理过程 pre_process --->view -->after_process

1 pre_process 2 pre_process 3 pre_process ------>view()

1.after_process.............------>client

中间件处理 不区分具体是哪个视图,对所有视图通通生效

  • before_first_request
    • 在处理第一个请求时执行
  • before_request
    • 在每次请求前处理
    • 如果在某修饰的函数中返回一个响应,试图函数将不再被调用.
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数,试图函数做出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数进行返回.
  • teardown_request
    • 在每次请求后执行
    • 接受一个参数,错误信息,如果有相关错误抛出
from flask import Flask

app = Flask(__name__)

@app.before_first_request
def before_first_request():
    print('before_first_request')

@app.before_request
def before_request():
    print('before_request')

@app.after_request
def after_request(response):
    print('after_request')
    response.headers['Content-Type'] = 'application/json'
    return response

@app.teardown_request
def teardown_request(response):
    print('teardown_request')

@app.route('/')
def index():
    print('视图函数被调用')
    return 'index'

结果第一次调用和第二次调用

before_first_request
before_request
视图函数被调用
after_request
teardown_request
127.0.0.1 - - [15/Oct/2021 17:06:48] "GET / HTTP/1.1" 200 -
before_request
视图函数被调用
after_request
teardown_request
  1. 上下文
    并发访问同一个视图 用的同一个代码 那么返回的是哪个 id?
    request 上下文 根据环境来的 环境上下文

1. 请求上下文

  • request
    • 封装了http请求的内容,针对的是htpp请求举例 user=request.args.get('user'),获取请求的参数
    • 你在A线程取出来的数据只和A有关系 ,B和B有关系
  • session
    • 用来记录请求会话中的信息.针对的是用户信息.距离.session['name']=user.id 可以保存信息,也可以通过session.get['name']获取用户信息

2. 应用上下文

  • current_app
    • 意思当前的app 指的是定义的app=Flask(name)
    • 有些时候文件是多文件 要想获取app 比较不方便

current示例

from flask import Flask, request, abort

app = Flask(__name__)

app.redis_cli = 'redis client'

# /articles?channel_id=4353
@app.route('/articles')
def get_articles():
    channel_id = request.args.get('channel_id')
    print(app.redis_cli)
    if channel_id is None:
        abort(400) # 400 是代表bad request
    return 'get articles is {}'.format(channel_id)

from passport import bp

app.register_blueprint(bp)
from flask import Blueprint, current_app

bp = Blueprint('passport', __name__)

@bp.route('/bp')
def viewfunc():
    print(current_app.redis_cli)
    return 'ok'
  • g
    g对象就是个容器,是个仓库
# def db_query(user_id,user_name):
#     print('user_id={},user_name={}'.format(user_id,user_name))
#
def db_query():
    user_id=g.user_id
    user_name=g.user_name
    print('user_id={},user_name={}'.format(user_id,user_name))

@app.route('/')
def get_user_profile():
    user_id = 123
    user_name = '小明'
    g.user_id=user_id
    g.user_name=user_name
    db_query()
    return 'hello,world'

这样在访问根路径,

10. 上下文综合案例

分析

  • 特定强制需求 - > 装饰器
  • 所有试图的需求 - > 请求钩子

请求 -> 请求钩子(判断身份,对于未登录用户不做处理 放行)

  • 普通视图处理

强制登录视图—> 装饰器

g.user_id=123 g.user_id=None

三次函数被一次请求调用

用g对象调用传递

from flask import Flask, request, abort, current_app, g

app = Flask(__name__)

# 请求钩子(尝试判断用户身份,对于未登录的用户不做处理 放行)并用g对象保存用户身份信息
@app.before_request  # 每个视图都要调用
def authentication():
    '''
    利用before_request请求钩子 在进入所有视图前先尝试判断用户身份
    :return:
    '''
    # todo: 此处利用鉴权机制(如cookie,session,jwt等)鉴别用户信息
    # if 已登录用户 用户有身份信息
    g.user_id = 123
    # else 未登录用户, 用户无身份信息
    # g.user_id = None

# 强制登录装饰器
def login_required(func):
    def wrapper(*args, **kwargs):
        # 判断用户是否登录
        if g.user_id is None:
            abort(401)
        else:
            # 已登录
            return func(*args, **kwargs)

    return wrapper

@app.route('/')
def index():
    return 'home page user_id={}'.format(g.user_id)

# 上层装饰器的主题 是下面一个主题
@app.route('/profile')
# 碍着谁装饰谁
@login_required
def get_user_profile():
    return 'user_profile page user_id={}'.format(g.user_id)

ps: g对象每次请求结束都会重设这个变量

上下文实现原理 -> Threadlocal 线程局部变量

from flask import request

request -> 全局变量

/articles?channel_id=123 ->request.args.get('channel_id') ->123 Thread idA

/articles?channel_id=123 ->request.args.get('channel_id') ->124 Thred id B

request.args = {

'thread_a_id':123,

'thread_b_id':124

}(存了多个数据 字典)

posted @ 2022-04-10 23:40  始識  阅读(69)  评论(0)    收藏  举报