Python Flask框架 入门详解与进阶

1.Flask框架 入门

flask是一个非常轻量化的后端框架,与django相比,它拥有更加简洁的框架。django功能全而强大,它内置了很多库包括路由,表单,模板,基本数据库管理等。flask框架只包含了两个核心库(Jinja2 模板引擎和 Werkzeug WSGI 工具集),需要什么库只需要外部引入即可,让开发者更随心所欲的开发应用
Flask文档
中文文档(http://docs.jinkan.org/docs/flask
英文文档(http://flask.pocoo.org/docs/1.0/

2.环境搭建

2.1.安装flask

1.创建虚拟环境

mkvirtualenv flask -p python3

2.激活虚拟环境

workon flask

3.安装flask

pip install flask

2.2.flask程序编写

1.创建test.py文件

# 导入Flask类
from flask import Flask

#Flask类接收一个参数__name__
app = Flask(__name__)

# 装饰器的作用是将路由映射到视图函数index
@app.route('/')
def index():
    return 'Hello World'

# Flask应用程序实例的run方法启动WEB服务器
if __name__ == '__main__':
    app.run()

2.启动运行

python test.py

2.3.参数说明

1.Flask对象的初始化参数

Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块)

Flask 应用程序在创建的时候一些需要我们关注的参数:

import_name
Flask程序所在的包(模块),传__name__就可以
其可以决定 Flask 在访问静态文件时查找的路径
static_url_path
静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder
静态文件存储的文件夹,可以不传,默认为 static
template_folder
模板文件存储的文件夹,可以不传,默认为 templates

默认参数情况

app = Flask(__name__)

文件目录
文件目录
访问http://127.0.0.1:5000/static/fu.png就可以访问到图片
完整参数情况下

# 导入Flask类
from flask import Flask

#Flask类接收一个参数__name__,static_url_path是静态文件访问路径,static_folder静态文件存储的文件夹
app = Flask(__name__, static_url_path='w',static_folder='static_file')

# 装饰器的作用是将路由映射到视图函数index
@app.route('/')
def index():
    return 'Hello World'
    
if __name__ == '__main__':
    app.run()

文件目录
在这里插入图片描述

此时访问http://127.0.0.1:5000/w/IMG_5311.jpg,才可以访问到图片

2.应用程序配置参数

对于Flask对象初始化参数仅仅设置的是Flask本身的属性,比如:
Flask从哪里读取静态文件,Flask从哪里读取模板文件

还需要应用程序配置参数设置的,是一个Web应用工程的相关信息,比如:
数据库的连接信息,日志的配置信息,自定义的配置信息

使用方式
Flask将配置信息保存到了app.config属性中
读取
app.config.get(name)
app.config[name]
设置

第一种 :从配置对象中加载 app.config.from_object(DefaultConfig)

test_config.py

# 导入Flask类
from flask import Flask

# 配置对象方式加载配置信息
class DefaultConfig(object):
    """
    默认配置信息
    """
    SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'

#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')

# 设置
app.config.from_object(DefaultConfig)

#定义视图
@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'Hello World'

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

运行程序在后台就会打印出来 SECRET_KEY

第二种:从配置文件中加载

在当前目前创建一个setting.py文件,里面写入配置信息

SECRET_KEY = '我是配置文件信息'

test_config.py

# 导入Flask类
from flask import Flask

# 配置对象方式加载配置信息
class DefaultConfig(object):
    """
    默认配置信息
    """
    SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'

#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')

# 设置
# app.config.from_object(DefaultConfig)
app.config.from_pyfile('setting.py')

#定义视图
@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'Hello World'

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

运行就会打印出配置文件的信息

第三种:从环境变量中加载
在Linux系统中设置和读取环境变量的方式
export 变量名=变量值  # 设置
echo $变量名  # 读取

# 例如
export MYCONFIG=python
echo $MYCONFIG

在windows系统里是
set 变量名=变量值

test_config.py

# 导入Flask类
from flask import Flask

# 配置对象方式加载配置信息
class DefaultConfig(object):
    """
    默认配置信息
    """
    SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'

#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')

# 设置
# app.config.from_object(DefaultConfig)
# app.config.from_pyfile('setting.py')

 # silent=False 表示不安静的处理,没有值时报错通知,默认为False
 # silent=True 表示安静的处理,即使没有值也让Flask正常的运行下去
app.config.from_envvar('ENV_SETTING', silent=True)

#定义视图
@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'Hello World'

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

Flask使用环境变量加载配置就是通过环境变量值找到配置文件,再读取配置文件的信息,其使用方式为
我用windows来演示

在这里插入图片描述
Pycharm设置环境变量
在这里插入图片描述

在这里插入图片描述

项目中的常用方式
  • app.config.from_object(配置对象)
    优点:继承,复用
    缺点:敏感信息暴露
  • app.config.from_pyfile(配置文件)
    优点:独立文件,保护敏感数据
    缺点:不能继承,文件路径固定不灵活
  • app.config.from_envvar(环境变量)
    优点:独立文件,保护敏感数据,文件路径不固定,灵活
    缺点:不方便,记得设置环境变量
from flask import Flask

def create_flask_app(config):
    """
    创建Flask应用
    :param config: 配置对象
    :return: Flask应用
    """
    app = Flask(__name__)
    app.config.from_object(config) # 先从配置对象中加载默认配置信息

    # 从环境变量指向的配置文件中读取的配置信息会覆盖掉从配置对象中加载的同名参数
    app.config.from_envvar("ENV_SETTING", silent=True)
    return app

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

class DevelopmentConfig(DefaultConfig):
    DEBUG=True

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

@app.route("/")
def index():
    print(app.config['SECRET_KEY'])
    return "hello world"
app.run 参数

指定运行的主机IP地址,端口,是否开启调试模式

app.run(host="0.0.0.0", port=6000, debug = True)

2.4开发服务器启动方式

方式一:新式的终端运行

hello.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"
export FLASK_APP=hello
flask run
Running on http://127.0.0.1:5000/
  • 环境变量 FLASK_APP 指明flask的启动实例

  • flask run -h 0.0.0.0 -p 8000 绑定地址 端口

  • flask run --help获取帮助

  • 生产模式与开发模式的控制
    通过FLASK_ENV环境变量指明
    export FLASK_ENV=production 运行在生产模式,未指明则默认为此方式
    export FLASK_ENV=development运行在开发模式

方式二:Pycharm启动

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

3.路由与蓝图

1.路由

1.查询路由信息

方式1:命令行方式
flask routes
Endpoint  Methods  Rule                       # Endpoint: 视图函数名字,Rule: 路径
--------  -------  -----------------------
index     GET      /
static    GET      /static/<path:filename>
  • 在程序中获取
print(app.url_map)
方式2:在程序读取路由信息
for rule in app.url_map.iter_rules():
    print('name={} path={}'.format(rule.endpoint, rule.rule))
    # iter_rules返回一个列表
    # rule.endpoint视图的名字
    # rule.rule视图的路径

以json的方式返回应用内的所有路由信息

 主视图,返回所有视图网址
    """
    rules_iterator = app.url_map.iter_rules()
    return json.dumps({rule.endpoint: rule.rule for rule in rules_iterator})
    # json.dumps()是把python对象转换成json对象的一个过程,生成的是字符串

2.指定请求方式

  • GET
  • OPTIONS(自带) -> 简介版的GET请求,用于询问服务器接口信息
  • HEAD(自带)

cors跨域解决 ,前端访问后端接口时,先发一个options请求,后端允许请求,option是询问接口的特征的,不涉及真实的业务数据,比如接口的允许方式,允许的请求源头,然后浏览器发真才发真正的GET请求
那django-cors是在中间件中拦截处理了option请求

methods参数可以自己指定一个接口的请求方式

@app.route("/hello", methods=["GET", "POST"])
def view_func_2():
    return "hello"

2.蓝图

  • 一个应用可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user”、“/goods”
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint
  • 但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中

1.使用方式,分三步

  • 单一文件中,蓝图对象与定义视图放到一个文件中
from flask import Flask, Blueprint
app = Flask(__name__)
user_bp = Blueprint('user', __name__) # 1.创建一个蓝图对象

@user_bp.route('/hello')  # 2.设置视图的时候,由蓝图对象的路由装饰器进行绑定
def index():
    return "hello 444world"
app.register_blueprint(user_bp, url_prefix='/user') # 3.在应用对象上注册这个蓝图对象

url_prefix表示需要访问的路径/hello前加一个前缀user ,访问的路径:
http://127.0.0.1:5000/user/hello
在这里插入图片描述

  • 目录(包)蓝图中使用

    新建一个目录goods, 通常将创建蓝图对象放到Python包的__init__.py文件中  
    

init.py

from flask import Blueprint

goods_bp = Blueprint('good', __name__)

from . import view

view.py

from . import goods_bp

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


主程序文件中 注册蓝图

from flask import Flask, Blueprint
user_bp = Blueprint('user', __name__)
app = Flask(__name__)
@user_bp.route('/hello')
def index():
    return "hello 4444world"
app.register_blueprint(user_bp, url_prefix='/user')

from goods import goods_bp
app.register_blueprint(goods_bp)

目录结构
在这里插入图片描述

  • 扩展用法
app.register_blueprint(user_bp, url_prefix='/user') # url_prefix蓝图的前缀
admin = Blueprint("admin",__name__,static_folder='static_admin')  # static_folder设置蓝图的静态目录
admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib') # static_url_path是静态访问路径
admin = Blueprint('admin',__name__,template_folder='my_templates') # template_folder蓝图内部模板目录

4.请求与响应

4.1. URL路径参数(动态路由)请求

1.Flask提供的类型的转换器

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

比如

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


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

2.自定义转换器

1.创建转换器类,保存匹配时的正则表达式
from werkzeug.routing import BaseConverter

class MobileConverter(BaseConverter):
    """
    手机号格式
    """
    regex = r'1[3-9]\d{9}'
2. 自定义的转换器告知Flask应用
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: mobile
app.url_map.converters['mobile'] = MobileConverter
3.在使用转换器的地方定义使用
@app.route(('/send_code/<mobile:mobile_num>'))
def get_mobile(mobile_num):
     return "get mobile number:{}".format(mobile_num)

3. 其它的参数请求

不同位置的参数都存放在request的不同属性中

属性说明类型
data记录请求的数据,并转换为字符串*
form记录请求中的表单数据MultiDict
args记录请求中的查询参数MultiDict
cookies记录请求中的cookie信息Dict
headers记录请求中的报文EnvironHeaders
method记录请求使用的HTTP方法GET/POST
url记录请求的URL地址string
files记录请求上传的文件*
  • 实例1.想要获取请求/user?channel=123456666中channel的参数
from flask import Flask,request
app=Flask(__name__)

@app.route('/user')
def get_user():
     channel = request.args.get('channel')
     return "get user id {}".format(channel)

运行结果
在这里插入图片描述

  • 实例2.上传图片
from flask import Flask,request

app=Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_image():
     f = request.files['picture']
     with open('./img.jpg', 'wb') as new_jpg:
          new_jpg.write(f.read())

     return 'ok'

postman测试,如下图
在这里插入图片描述
flask给我们封装了一个save方法 不需要with open方法

from flask import Flask,request

app=Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_image():
     f = request.files['picture']
     # with open('./img.jpg', 'wb') as new_jpg:
     #      new_jpg.write(f.read())
     f.save('./image.jpg')
     return 'ok'

4.2.处理响应

在不同的场景里返回不同的响应信息

1 使用render_template方法渲染模板并返回

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
模板html内容
<h1>{{ user_name }}</h1>
<h1>{{ user_id}}</h1>
</body>
</html>

如图 templates模板文件夹下新建的一个index.html
在这里插入图片描述

后端视图

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    username = 'zhangsan'
    userid = 12
    return render_template('index.html', user_name=username, user_id=userid)

在这里插入图片描述

扩展:后面视图这样也可以

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    data = {'user_name':'zhangsan', 'user_id':12}
    # return render_template('index.html', user_name=username, user_id=userid)
    return render_template('index.html', **data)
    # **data等价于 user_name='zhangsan', user_id=12

2.重定向

from flask import redirect

@app.route('/demo2')
def demo2():
    return redirect('http://www.baidu.com')

3.返回JSON

  • json.dumps() 仅仅是把数据转换为json格式
  • jsonify 1.转换成json格式字符串,设置了响应头Content-Type:application/json
from flask import jsonify

@app.route('/demo3')
def demo3():
    json_dict = {
        "user_id": 10,
        "user_name": "laowang"
    }
    return jsonify(json_dict)

4 自定义状态码和响应头

1.元组方式

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

@app.route('/article')
def demo4():
    # return 'hello world 666', 560
    # return 'hello world 666', 560, [('user_name', 'lisi')]
    return 'hello world 666', 560, {'user_name': 'lisi'}
2. make_response方式
@app.route('/article2')
def demo5():
    resp = make_response('make response测试')
        resp.headers[“user_name”] = “zhangsan”
        resp.status =404 not found”
    return resp

4.3.Cookie与Session

1.Cookie

  • 设置
from flask import Flask, make_response

app = Flask(__name__)

@app.route('/cookie')
def set_cookie():
    resp = make_response('set cookie ok')
    resp.set_cookie('username', 'wangwu')
    return resp

浏览器里查看设置的信息
在这里插入图片描述

  • 设置有效期
from flask import Flask, make_response

app = Flask(__name__)

@app.route('/cookie')
def set_cookie():
    resp = make_response('set cookie ok')
    resp.set_cookie('username', 'wangwu', max_age=3600)
    return resp
  • 读取
from flask import request

@app.route('/get_cookie')
def get_cookie():
    resp = request.cookies.get('username')
    return resp
  • 删除
from flask import request

@app.route('/delete_cookie')
def delete_cookie():
    response = make_response('hello world')
    response.delete_cookie('username')
    return response

2.Session

  • 需要先设置SECRET_KEY
class DefaultConfig(object):
    SECRET_KEY = 'ddkslslgcd;slslgdd 98877777dds'

app.config.from_object(DefaultConfig)

或者直接设置
app.secret_key='ddkslslgcd;slslgdd 98877777dds'
  • 设置
@app.route('/set_session')
def set_session():
    session['username'] = 'lisi'
    return 'ok'
  • 读取
@app.route('/get_session')
def get_session():
    username = session.get('username')
    return 'get session username:{}'.format(username)

5.异常、请求勾子与上下文

5.1.异常处理

1.HTTP 异常主动抛出

  • abort 方法
abort(404)  # 抛出状态码的话,只能抛出 HTTP 协议的错误状态码

2.捕获错误

  • errorhandler 装饰器
    注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    return '除数不能为0'
@app.errorhandler(500)
def internal_server_error(e):
    return '服务器搬家了'

5.2.请求钩子

请求钩子就是起到中间件或者中间层的作用
假如有多个中间层,每个中间层可以理解为一个类,当一个具体的请求过来是,先去找中间件,DJANGO的处理过程

# [ middleware1  ->  Class Middleware1
#                         def pre_process
#                         def after_process(response)
# middleware2
# middleware3]
# 请求的处理过程  pre_process  -> view -> after_process
# request 请求支持 处理流程
# middleware1.pre_process() -> middleware2.pre_process() -> middleware3.pre_process()
# -> view() -> middleware3.after_process() -> middleware2.after_process() -> middleware1.after_process() -> client
# 中间件处理 不区分具体是哪个视图 ,对所有视图通通生效

Flask支持如下四种请求钩子

  • before_first_request
    在处理第一个请求前执行

  • before_request
    在每次请求前执行
    如果在某修饰的函数中返回了一个响应,视图函数将不再被调用

  • after_request
    如果没有抛出错误,在每次请求后执行
    接受一个参数:视图函数作出的响应
    在此函数中可以对响应值在返回之前做最后一步修改处理
    需要将参数中的响应在此参数中进行返回

  • teardown_request:
    在每次请求后执行
    接受一个参数:错误信息,如果有相关错误抛出

代码测试

from flask import Flask
from flask import abort
app = Flask(__name__)

# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
    print("before_first_request")


# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
    print("before_request")
    # if 请求不符合条件:
    #     return "laowang"


# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@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('view 视图已执行')
    return 'index'

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

5.3.上下文

Flask中有两种上下文,请求上下文和应用上下文

Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

1 请求上下文(request context)

  • request
    封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
  • session
    用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。

2 应用上下文(application context)

它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的

2.1.current_app
# 导入Flask类
from flask import Flask, current_app

#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')
app.redis_cli='redis-cli 888'

# 装饰器的作用是将路由映射到视图函数index
#定义视图
@app.route('/')
def index():
    return  current_app.redis_cli

if __name__ == '__main__':
    app.run()
2.2.g对象

g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。

示例

from flask import Flask, g

app = Flask(__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():
    g.user_id = 123
    g.user_name = 'itcast'
    db_query()
    return 'hello world'

g对象与请求钩子的综合案例

from flask import Flask, abort, g

app = Flask(__name__)

@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 not None:
            return func(*args, **kwargs)
        else:
            abort(401)

    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)
2.3.app_context 与 request_context
  • app_context
    app_context为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_app、g
    可以通过with语句进行使用
>>> from flask import Flask
>>> app = Flask('')
>>> app.redis_cli = 'redis client'
>>> 
>>> from flask import current_app
>>> current_app.redis_cli   # 错误,没有上下文环境
报错
>>> with app.app_context():  # 借助with语句使用app_context创建应用上下文
...     print(current_app.redis_cli)
...
redis client
  • request_context
    request_context为我们提供了请求上下文环境,允许我们在外部使用请求上下文request、session
    可以通过with语句进行使用
>>> from flask import Flask
>>> app = Flask('')
>>> request.args  # 错误,没有上下文环境
报错
>>> environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}  # 模拟解析客户端请求之后的wsgi字典数据
>>> with app.request_context(environ):  # 借助with语句使用request_context创建请求上下文
...     print(request.path)
...   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纬领网络

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值