Loading

Flask

Flask

一、介绍和安装

Flask是一个基于WSGI协议的轻量级web框架,它使用起来非常简单且快捷,并且有能力扩展到开发大型项目。它基于 Werkzeug and Jinja 开发,已经成为目前流行的Python web应用程序框架之一。Flask为开发者提供了一些建议,但是并不会强制依赖某些布局,由开发人员自己来选择他们想要使用的工具和库。在社区提供了许多Flask的扩展包,这使添加新功能变得容易。

参考文档:官方文档中文文档
参考文章:https://www.cnblogs.com/wupeiqi/articles/7552008.html
源码:GitHub

安装:

pip install flask

二、基本使用

1 flask初识

先创建一个最简单的Flask应用

# 导入Flask类
from flask import Flask
# 创建一个该类的对象。第一个参数是应用模块或者包的名称。
# __name__ 是一个适用于大多数情况的快捷方式。有了这个参数, Flask 才能知道在哪里可以找到模板和静态文件等东西。
app = Flask(__name__)

# 然后我们使用route()装饰器来告诉 Flask 触发函数的URL,这里是根路径
@app.route("/")
def hello_world():
    # 函数返回值就是浏览器中显示的信息。
    # 默认的内容类型是HTML,因此字符串中的HTML代码会被浏览器渲染。
    return "<p>Hello, World!</p>"
# 这里写在文件里启动,(还可以命令行启动)
app.run()

这样就启动了一个非常简单的服务器,默认flask会监听本地的5000端口,打开浏览器输入http://127.0.0.1:5000/即可看到hello world。接下来对比django,看看flask中如何使用。

2 三种类型的响应

在django中,有render,HttpResponse,redirect三种类型的响应,在flask中同样也有

from flask import Flask,render_template,redirect
app = Flask(__name__)
# 1.返回字符串就,对应django的HTTPResponse
@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>" 

# 2.返回模版文件render_template,对应django的render
@app.route("/")
def hello_world():
    # 这里的index.html模版文件默认路径是根路径下的templates目录下(是Flask类的init方法中默认值,可以另行指定)
    return render_template("index.html")

# 3.redirect,对应于django的redirect
@app.route("/")
def hello_world():
    return redirect("https://www.baidu.com")

3 模版语法

flask中同样有模版语法,django的模版语法是自己写的,flask使用的是jinja模块,它更好用一些。在HTML中双大括号中的变量可以被动态替换,在点.调用的同时,jinja还可以直接调用方法,并且可以括号取值、get取值。使用和django类似,

3.1 举例

demo.py

from flask import Flask,render_template,request,redirect
app = Flask(__name__)
USERS = {
    1:{'name':'令狐冲','age':20,'gender':'男','text':"独孤九剑"},
    2:{'name':'小龙女','age':18,'gender':'女','text':"玉女心经"},
    3:{'name':'段誉','age':23,'gender':'男','text':"凌波微步"},
}

@app.route('/index',methods=['GET'])
def index():
    return render_template('index.html',user_dict=USERS) # 可以直接在这里加自定义的键值

index.html

<body>
    <h1>列表</h1>
    <table>
        <!--循环结构,与django不同的是,可以直接加括号调用方法-->
        {% for k,v in user_dict.items() %}
        <tr>
            <!--对于字典内的数据,可以`.`取值,也可以中括号`[]`,还可以使用`get`方法-->
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['age']}}</td>
            <td>{{v.get('gender')}}</td>
            <!--if判断-->
            {% if k==2 %}
                <td>秘笈!{{ secret }}</td>
            {% endif %}
        </tr>
        {% endfor %}
    </table>
</body>

3.2 总结:

变量

{{ 变量名 }}

循环

{% for i in var %}

 ...

{% endfor %}

条件

{% if ... %}

 ..

{% endif %}

4 请求方法、request对象、反向解析

4.1 举例

demo.py

from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/login',methods=['GET','POST'],endpoint='log')  # endpoint路由的别名,用作反向解析
def login():
    # request.method判断请求类型
    if request.method == "GET":
        params = request.query_string # get请求携带的数据从query_string取出,默认是字节byte类型
        print(params)
        return render_template("login.html")
    else:
        print(request.form)	# form表单提交的post请求数据从form取出
        user = request.form.get('user') # 可以使用get取值
        pwd = request.form.get('pwd')
        if user == 'abc' and pwd == '123':
            return "post"

login.html

<body>
    <h1>用户登录</h1>
    <form method="post">
        <input type="text" name="user">
        <input type="text" name="pwd">
        <input type="submit" value="登录">
    </form>
</body>

4.2 总结

使用methods参数,传递一个列表,指定接收什么类型的请求,比如GET,POST

endpoint用作反向解析的关键字(别名)。使用url_for作后端反向解析,根据endpoint设置的值获取url。

request对象导入即可使用,能够自动区别每次的请求对象,通过request.method判断请求类型,

request.query_string获取get请求携带参数,request.form.get获取form表单提交的post请求数据。

5 路由的另一种形式

flask通过app.route装饰器来实现路由分发,这是它的源码

def route(self, rule: str, **options: t.Any) -> t.Callable:
    def decorator(f: t.Callable) -> t.Callable:
        endpoint = options.pop("endpoint", None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

举例:@app.route('/login',methods=['GET','POST'],endpoint='log'),首先调用route函数返回装饰器,然后将视图函数作为参数传到decorator中,然后调用self.add_url_rule方法,此时的self是Flask类的对象(app对象),即Flask.add_url_rule方法。所以,可以不使用装饰器,而改为类似django的路由分发形式:

from flask import Flask,render_template,redirect

app = Flask(__name__)

def hello_world():
    return "hello!"
# 手动调用add_url_rule方法,把url地址、视图函数、反向解析别名传入
app.add_url_rule("/",view_func=hello_world,endpoint="root")

app.run()

访问 http://127.0.0.1:5000/同样生效。这和django中的url('/',xxxx.view,name="root")的形式很相似。

6 配置文件与配置方法

flask中的配置文件是一个flask.config.Config,它继承字典,可以像字典一样操作。默认配置为:

{
    'DEBUG':                                get_debug_flag(default=False),  # 是否开启Debug模式
    'TESTING':                              False,                          # 是否开启测试模式
    'PROPAGATE_EXCEPTIONS':                 None,                          
    'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
    'SECRET_KEY':                           None,							# 密钥
    'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
    'USE_X_SENDFILE':                       False,
    'LOGGER_NAME':                          None,
    'LOGGER_HANDLER_POLICY':               'always',
    'SERVER_NAME':                          None,
    'APPLICATION_ROOT':                     None,
    'SESSION_COOKIE_NAME':                  'session',
    'SESSION_COOKIE_DOMAIN':                None,
    'SESSION_COOKIE_PATH':                  None,
    'SESSION_COOKIE_HTTPONLY':              True,
    'SESSION_COOKIE_SECURE':                False,
    'SESSION_REFRESH_EACH_REQUEST':         True,
    'MAX_CONTENT_LENGTH':                   None,
    'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
    'TRAP_BAD_REQUEST_ERRORS':              False,
    'TRAP_HTTP_EXCEPTIONS':                 False,
    'EXPLAIN_TEMPLATE_LOADING':             False,
    'PREFERRED_URL_SCHEME':                 'http',
    'JSON_AS_ASCII':                        True,
    'JSON_SORT_KEYS':                       True,
    'JSONIFY_PRETTYPRINT_REGULAR':          True,
    'JSONIFY_MIMETYPE':                     'application/json',
    'TEMPLATES_AUTO_RELOAD':                None,
}

6.1 配置方法1

直接写在项目里,如果是小项目,可以使用;对于大型项目,不建议采用这种方式。

app.config['DEBUG'] = True
# 或者使用update(config是字典的子类)
app.config.update(...)

6.2 配置方法2

通过py文件配置

# 第一个参数是py文件路径,可以是绝对路径,也可以是相对路径
app.config.from_pyfile("settings.py") # 这里是相对路径

settings.py

DEBUG = True

6.3 配置方法3

通过对象来配置

# 第一个参数可以是
#	- 字符串:在这种情况下,字符串对应的对象会被导入
#	- 对象:直接传入一个对象
app.config.from_object('settings.TestingConfig')

settings.py

class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URI = 'sqlite://:memory:'

class ProductionConfig(Config):
    # 生产环境
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    # 开发环境
    DEBUG = True

class TestingConfig(Config):
    # 测试环境
    TESTING = True

这种方式的好处在于,事先定义好每个环境的配置文件,然后只需要在app.config.from_object()配置就能切换。

6.4 其他配置方法

通过文件、json、环境变量配置,详见这里

7 路由层转换器和参数

7.1 转换器

flask有内置的转换器,和django2.x+版本的转换器效果相同

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter, # 如果省略,默认是string
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

使用方法:

@app.route('/post/<int:post_id>') # 访问127.0.0.1/post/1
@app.route('/post/<post_id>')  # 省略转换器,默认使用string

7.2 参数

app.route中的参数,和app.add_url_rule中参数一样

rule   # 字符串,匹配的url,支持转换器
view_func=None  # 视图函数
endpoint  # 视图函数别名,用于反向生成URL(使用url_for),如果不写默认为当前视图函数的名称,注意,不要与其它视图函数重名
methods  #  允许哪些请求方式,默认为GET;
defaults  # 使用defaults={'k1':'v1'}传值,视图函数加参数k1可以通过k1获取到v1
strict_slashes  #  对URL最后的/符号是否严格要求,如果为False则不严格。
redirect_to  # 重定向到指定地址,可以是字符串url地址,或者是可调用的函数(函数返回值是url地址)
subdomain  # 允许哪些子域访问

flask本质调用了werkzeug的Rule的init方法,所以更多详情详见这里

8 flask的CBV

原理和django的CBV基本相同

from flask import Flask
from flask import views # 导入基类
app=Flask(__name__)

class LoginView(views.View):
     # 需要重写dispatch_request方法
     methods = ['GET']

     def dispatch_request(self):
         return 'xxx'
# 这里和django不同的是必须要传一个视图函数的别名,也就是endpoint
app.add_url_rule('/login',view_func=LoginView.as_view('login')) 

也可以直接继承MethodView,就不用手动重写dispatch_request方法了。

def outer(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result
    return inner

class LoginView(views.MethodView):
    methods = ['GET']  # 指定允许的请求方式
    decorators = [outer, ]  # 可以增加多个装饰器,内部是for循环执行的,和装饰器从上到下一样
    
    def get(self):
        print('xxxxx')
        return "get"
    def post(self):
       return 'post'

app.add_url_rule('/login',view_func=LoginView.as_view('login')) 

9 pycharm设置jinja模版自动补全

第一步,右键模版文件夹--->Mark Directory as--->Template Folder,此时可能会提示你还未选模板语言,是否要选择一个?,点击确定,

image

如果没有提示也没关系,打开设置,找到下图位置,选择jinja2即可

image

10 request请求对象方法

request.method   # 请求方法
request.args  # get请求携带的数据
request.form  # post请求提交的数据
request.values  # post和get提交的所有数据
request.cookies  # 浏览器携带的cookie
request.headers  # 请求头
request.path  # 不带域名,请求地址,不带参数
request.full_path  # 不带域名,请求地址,带参数
request.url  # 带域名带参数的请求地址
request.base_url # 带域名不带参数请求地址
request.url_root # 域名
request.host_url # 域名
request.host # 127.0.0.1:5000
request.files # 文件

11 response响应对象

return "字符串"  # 直接返回字符串
return render_template('html模板路径',**{}) # 渲染模版,可以传自定义键值
return redirect('/index.html') # 重定向
return jsonify({'k1':'v1'})  # json格式响应,相当于django的Jsonresponse
# make_response用于响应头里添加自定义信息,比如session、cookies,使用这个方法包装一个response对象,比如下面的例子:
# response是flask.wrappers.Response对象
#         def index():
#            response = make_response(render_template('index.html', foo=42))
#            response.headers['X-Parachutes'] = 'parachutes are cool'
#            return response


response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response

12 session

from flask import Flask,sessions

在使用session之前需要在配置文件中设置SECRET_KEY

然后在视图函数中直接使用

session['key']=value  # 设置session
session.pop('key')  # 删除session
session['key']  # 获取session


13 消息闪现

闪现系统的基本工作方式是:在且只在下一个请求中访问上一个请求结束时记录的消息。

flash("xxxx") # 用于闪现一个消息
get_flashed_message() # 用于获取消息

在a页面设置flash,然后在b页面获取,并且只能获取一次。一般结合布局模板来使用闪现系统。此外还支持分类,更多内容详见这里

14 请求扩展

相当于django的中间件。

from flask import Flask, Request

app = Flask(__name__, template_folder='templates')
app.debug = True

# 仅第一次请求来的时候会执行,后面再来的请求不会执行
@app.before_first_request
def before_first_request1():
    print('before_first_request1')


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

# 类似于django的process_request,请求来之前做一些操作(比如登录认证)
@app.before_request  
def before_request1():
    Request.nnn = 123
    print('before_request1')


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

# 请求走之前执行,可以在这里做一些操作(比如设置session等)
@app.after_request
def after_request1(response):
    print('before_request1', response)
    return response


@app.after_request
def after_request2(response):
    print('before_request2', response)
    return response

# 只要出现404状态码,就交由该视图处理,可以render一个页面
@app.errorhandler(404)
def page_not_found(error):
    return 'This page does not exist', 404

# 全局的模版,{{sb(1,2)}}  
@app.template_global()
def sb(a1, a2):
    return a1 + a2

# 全局过滤器,{{ 1|db(2,3)}}
@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3

@app.route('/')
def hello_world():
    return "helloworld"


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

15 蓝图

为了在一个或多个应用中,使应用模块化并且支持常用方案,Flask 引入了蓝图概念,主要是为应用提供目录划分,类似于django的多个app注册。

多个应用注册时,可以不使用蓝图,但是这样会导致每个app都使用自己独立的配置,且只能在 WSGI 层中管理应用。而如果使用蓝图,那么应用会在Flask层中进行管理,共享配置,通过注册按需改变应用对象。蓝图的缺点是一旦应用被创建后,只有销毁整个应用对象才能注销蓝图。

要使用蓝图,首先要注册蓝图

from flask import Flask
from yourapplication.simple_page import simple_page
# 正常创建app,在app里注册蓝图
app = Flask(__name__)
app.register_blueprint(simple_page)

然后在应用下使用蓝图

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

# 使用时,用Blueprint代替Flask创建应用
simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates',static_folder='statics',static_url_path='/static')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template(f'pages/{page}.html')
    except TemplateNotFound:
        abort(404)

一般来说把项目划分成几个部分,比如模版、视图、静态文件、中间件等等,使用蓝图注册视图层下的应用,统一管理。

蓝图的应用示例,参考这里

大型项目的目录结构:点击下载

简单项目的目录结构:点击下载

16 信号

Flask 自 0.6 版本开始在内部支持信号。信号功能由优秀的 blinker 库提供支持, 如果没有安装该库就无法使用信号功能,但不影响其他功能。

什么是信号?当flask的运行过程中或者是在flask扩展中发生动作时,会设置一些信号,默认是不会执行的。当你需要使用信号的时候,订阅某个信号,为它绑定你的函数,它就会执行,并且所有的信号处理器是乱序执行的。

flask内置了如下信号

template_rendered = _signals.signal('template-rendered') # 模板渲染后执行 
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
appcontext_tearing_down = _signals.signal('appcontext-tearing-down') # 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flash在其中添加数据时,自动触发

信号的使用:

首先需要安装blinker

pip install blinker

然后只需要定义函数,订阅信号即可

from flask import Flask,signals # 导入信号类

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

# 创建函数
def func(*args, **kwargs):
    print('request_started信号触发了', args, kwargs) # args[0]是当前的app对象

# 订阅信号request_started,connect里传入定义的函数
signals.request_started.connect(func)

@app.route("/",endpoint="root")
def hello_world():
    print("视图函数执行")
    return "hello!"

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

支持自定义信号(需要自己触发信号,其余操作和内置信号使用相同):

from flask import Flask
from flask.signals import _signals

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

# 自定义信号
sig = _signals.signal('xxxx')

def func( *args, **kwargs):
    print(args)
    print(kwargs)

# 自定义信号中注册函数
sig.connect(func)

@app.route("/",endpoint="root")
def hello_world():
    # 在想要触发的位置send触发信号
    sig.send("abc",k="v")
    print("视图函数执行")
    return "hello!"

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

三、flask第三方插件

四、flask源码分析

posted @ 2021-11-21 22:51  yyyz  阅读(29)  评论(0编辑  收藏  举报