Flask 基础

Flask 基础

Flask 是一个使用 Python 编写的轻量级 Web 应用框架。基于 Werkzeug WSGI 工具箱和 Jinja2 模板引擎。

Flask 使用 BSD 授权。Flask 被称为“microframework”,因为它使用简单的核心,用 extension 增加其他功能。Flask 没有默认使用的数据库、窗体验证工具。然而,Flask 保留了扩增的弹性,可以用 Flask-extension 加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。

1 安装

pip3 install flask

2 基本使用

2.1 werkzeug

与 Django 类似,Flask 是基于 werkzeug 实现的。通过 werkzeug 即可简单实现最基本的返回数据。

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

def run(environ, strat_response):
    return [b"asdf"]


if __name__ == '__main__':

    run_simple("localhost", 4000, run)

亦可使用 Response 直接返回字符串,注意导入方式与 Django 不同。

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

@Request.application
def hello(request):
    return Response('Hello World')


if __name__ == '__main__':

    run_simple("localhost", 4000, hello)

2.2 使用 Flask

可将 Flask 看作一个类,通过 app=Flask(__name__) 进行实例化,app.run() 用以启动 Flask 。

在函数上增加 @app.route('/index') 将函数变成视图函数,而在 route 中即可定义访问该视图函数的路由。

from flask import Flask

app = Flask(__name__)

@app.route('/index')
def hello():
    return "Hello World"


if __name__ == '__main__':

    app.run()

2.3 Flask 实现简单的登录功能

学习 Flask 时可以类比着 Django 的学习。Django 中有的 request redirect session ,Flask 中也有,不过, 同样是实现渲染功能的 render 函数在 Flask 中变成了 render_template 。Flask 的传参方式与 Django 一样,都是直接赋值 user=user

Flask 默认只支持 GET 方式传数据,如果想要支持更多的数据传输方式,可在 route 中加上 methods 属性来设置。@app.route('/login', methods=["GET", "POST"])。如果存在 POST 请求,则需要设置 Flask 秘钥来确保传输安全。 app.secret_key = "fasd"

Flask 默认 HTML 文件放在 templates 文件中。

app.py

from flask import Flask, render_template, request, redirect, session


app = Flask(__name__)
# Flask的秘钥
app.secret_key = "fasd"

@app.route('/login', methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template('login.html')

    user = request.form.get('user')
    pwd = request.form.get('pwd')
    if user == 'ban' and pwd == 'ban':
        session['user'] = user
        return redirect('/index')

    return render_template('login.html', error="用户名或密码错误")

@app.route('/index')
def index():
    user = session.get('user')
    if not user:
        redirect('/login')

    return render_template('index.html', user=user)


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

3 配置文件

3.1 复习:通过字符串找类

给你一个路径 “settings.Foo”,可以找到类并获取去其中的大写的静态字段。

首先,使用 rsplit 函数将字符串分成模块和类,再使用 importlib 导入模块。然后使用 getattr 函数获取模块中的类。最后,通过 dir 函数获取类中的字段。

settings.py

settings.py
    class Foo:
        DEBUG = True
        TEST = True

xx.py

import importlib

path = "settings.Foo"

p,c = path.rsplit('.',maxsplit=1)
m = importlib.import_module(p)
cls = getattr(m,c)

# 找这个类中的字段
for key in dir(cls):
    if key.isupper():
        print(key,getattr(cls,key))

3.2 Flask 配置文件

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,
    }

1. 配置方式一

通过 app.config['属性']=值 的方式配置。app.config['DEBUG'] = True

由于 Config 对象本质上是字典,所以还可以使用 app.config.update(...) 与更新字典一致的操作进行配置。

2. 配置方式二

直接将配置信息写在 Python 文件中,用 app.config.from_pyfile("python文件名称") 的方式进行配置。

settings.py

 DEBUG = True

app.config.from_pyfile("settings.py")

或者  app.config.from_envvar("环境变量名称") ,其中环境变量的值为 Python 文件的名称。

3. 配置方式三

直接将配置信息写在 json 文件中,用 app.config.from_json("json文件名称") 的方式进行配置。且文件内必须是 json 格式,因为内部会执行 json.loads

以字典格式直接配置:app.config.from_mapping({'DEBUG':True})

4. 配置方式四(推荐)

Flask 支持以模块中类的方式引入配置信息。即将配置信息以字段与值的方式放到另外的文件里的类中,通过 app.config.from_object("python类或类的路径") 代码进行配置。

settings.py

from datetime import timedelta
class Config(object):
    DEBUG = False
    TESTING = False
    SECRET_KEY = "asdfasdfas23"
    DATABASE_URI = 'sqlite://:memory:'

    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
    PERMANENT_SESSION_LIFETIME = timedelta(hours=1)


class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'


class DevelopmentConfig(Config):
    DEBUG = True


class TestingConfig(Config):
    TESTING = True

app.py

app.config.from_object("settings.DevelopmentConfig")

这种导入配置信息的优点有:

  1. 更改配置信息方便,只需在运行文件中改一下配置信息类即可
    例如,开发时的配置信息和上线时的配置信息有所不同,采用这种方式配置,只需在文件中改下配置类名即可。
  2. 借助类可以继承的特性,使得配置信息也可以继承,从而减少冗余。

4 路由系统

前面已经介绍了 Flask 的路由系统由装饰器构成。并且介绍到可以在其中设置支持的数据传输方式: @app.route('/login', methods=['GET', 'POST'])

4.1 别名与反向获取 URL

在 route 函数中加上 endpoint 字段设置路由别名,如果没有设置 endpoint 字段,默认别名为函数名。

通过别名反向获取 URL 需要使用 url_for 函数。此函数形式为 url_for(endpoint, *args) 其中第一个参数为路由别名,第二个参数为要为 URL 传的值。注意,url_for 需要导入。

from flask import url_for

url_for('endpoint')

url_for("index",nid=777)

4.2 动态路由

我们常常需要在 url 中进行传值操作,比如 uid 等。此时就需要用到动态路由。

使用方式为 route('url/<类型:变量名>'),其中,类型和变量名之前的 ':' 不能有空格,如果省略类型则默认为字符串类型。

  • @app.route('/user/')
  • @app.route('/post/')
  • @app.route('/post/')
  • @app.route('/post/')
  • @app.route('/login', methods=['GET', 'POST'])

常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

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

5 请求与响应

5.1 请求相关信息

# 请求相关信息
# request.method
# request.args
# request.form
# request.values
# request.cookies
# request.headers
# request.path
# request.full_path
# request.script_root
# request.url
# request.base_url
# request.url_root
# request.host_url
# request.host
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))

5.2 响应相关

1. 响应体相关信息

  1. 返回字符串
    return “asdf"
  2. 返回 json 数据
from flask import jsonify

return jsonify({'k1':'v1'})
  1. 渲染模板
    return render_template('xxx.html')
  2. 跳转链接
    return redirect()

2. 定制响应头

响应由响应头和响应体组成,响应头可以先进行封装再返回。headers 字段来设置响应头中的字段值,此外还能设置 cookie 。

from flask import make_response

obj = make_response("asdf")
obj.headers['xxxxxxx'] = '123'
obj.set_cookie('key', 'value')
return obj

示例:

@app.route('/index')
def index():
    obj = make_response(render_template('index.html', stu_dic=STUDENT_DICT))
    obj.headers['xxxx'] = '123'
    return obj

response_header_DIY

6 示例程序:学生管理

6.1 基本功能实现

对学生信息进行详情查看或者删除。

app.py

#!/usr/bin/env python  
#-*- coding:utf-8 -*-  

from flask import Flask, render_template, request, redirect, session, url_for, make_response


app = Flask(__name__)

app.config.from_object("settings.DevelopmentConfig")
# Flask的秘钥
app.secret_key = "fasd"

STUDENT_DICT = {
    1:{'name':'王龙泰','age':38,'gender':'中'},
    2:{'name':'小东北','age':73,'gender':'男'},
    3:{'name':'田硕','age':84,'gender':'男'},
}


@app.route('/login', methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template('login.html')

    user = request.form.get('user')
    pwd = request.form.get('pwd')
    if user == 'ban' and pwd == 'ban':
        session['user'] = user
        return redirect('/index')

    return render_template('login.html', error="用户名或密码错误")


@app.route('/index')
def index():
    # obj = make_response(render_template('index.html', stu_dic=STUDENT_DICT))
    # obj.headers['xxxx'] = '123'
    # return obj
    return render_template('index.html',stu_dic=STUDENT_DICT)


@app.route('/detail/<int:nid>')
def detail(nid):
    info = STUDENT_DICT[nid]
    return render_template('detail.html', info=info)


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

idnex.html

<body>
    <h1>学生列表</h1>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>选项</th>
            </tr>
        </thead>
        <tbody>
            {% for k,v in  stu_dic.items() %}
                <tr>
                    <td>{{k}}</td>
                    <td>{{v.name }}</td>
                    <td>{{v.age}}</td>
                    <td>{{v.gender}}</td>
                    <td>
                        <a href="/detail/{{k}}">查看详细</a>
                        |
                        <a href="/delete/{{k}}">删除</a>

                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
</body>

detail.html

<body>
    <h1>学生详细</h1>
    <ul>

        {% for item in info.values() %}
        <li>{{item}}</li>
        {% endfor %}
    </ul>

login.html

<body>
<h1>欢迎登录</h1>
<form action="" method="post">
    <input type="text" name="user">
    <input type="password" name="pwd">
    <input type="submit" value="提交">{{error}}
</form>
</body>

6.2 添加登录认证

1. 方式一:if 判断(不推荐)

在每个视图函数中添加判断,如果 session 中没有登录相关信息则跳转到登录页面。

@app.route('/index')
def index():
    if not session.get('user'):
        return redirect(url_for('login'))
    return render_template('index.html',stu_dic=STUDENT_DICT)

这种方式必须得在每一个需要登录认证的视图函数中添加,太 low ,不推荐,也别用。

2. 方式二:装饰器

(1) 装饰器回顾

示例:

def auth(func):
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner

@auth
def index():
    print('index')

@auth
def detail():
    print('detail')

print(index.__name__)
print(detail.__name__)

此时打印两个函数名的结果全是 inner ,这是装饰器本身性质导致。

回到用装饰器对 Flask 视图函数进行登录认证上,因为 endpoint 默认为函数名,而函数名一致会使得反向 url 失效,怎么解决呢?对 inner 函数加上内置装饰器 @functools.wraps(func)

import functools

def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner

@auth
def index():
    print('index')

@auth
def detail():
    print('detail')

print(index.__name__)
print(detail.__name__)

此时输出为:

index
detail

(2) 登录认证用的装饰器

import functools
def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        if not session.get('user'):
            return redirect(url_for('login'))
        ret = func(*args,**kwargs)
        return ret
    return inner

此时的认证方法为:

@app.route('/index')
@auth
def index():
    return render_template('index.html',stu_dic=STUDENT_DICT)

注意:
Flask 的路由系统也是以装饰器方式来实现的。那么,路由系统装饰器和登录认证装饰器哪个在前,哪个在后呢?答案是登录认证装饰器在后。因为登录装饰器用到了路由系统装饰器中的内容。

该方法的应用场景:比较少的函数中需要额外添加功能。如果是所有函数都需要添加额外功能,那么推荐使用接下来所说明的第三种方式。

3. 方式三:before_request

before_request 也是一种 Flask 自带的装饰器,它用来在请求时进行一些操作,如果返回 None ,则继续进入视图函数做相应处理,如果返回其他,则作为响应返回结果。

用在登录认证时代码如下:

@app.before_request
def xxxxxx():
    if request.path == '/login':
        return None

    if session.get('user'):
        return None

    return redirect('/login')

7 模板渲染

7.1 数据操作

1. 基本数据类型

在 Flask 的模板中,对于传过来的数据可以执行 Python 语法,例如:dict.get() list['xx']

2. 传 HTML 代码

由于安全性考虑,从后端传来的 HTML 代码字符串不能直接在前端编译,而是显示为字符串,想要其编译有两种方式:

(1) 前端管道符加持: {{arg_html|safe}}

(2) 后端安全封装:MarkUp("<input ... />")

7.2 函数

1. 传入函数

def func(arg):
    return arg + 1

@app.route('/tpl')
def tpl():
    context = {
        'users':['longtai','liusong','zhoahuhu'],
        'txt':Markup("<input type='text' />"),
        'func':func
    }

    return render_template('tpl.html',**context)
{{func(6)}}

值得注意的是:

  1. Django 传过来的数据是自动执行的,所有不用加括号,而 Flask 不执行,所以需要加上括号。
  2. 当函数参数为多个时,前端传参数的不同。
    两个参数:{{sb(1,9)}}
    三个参数:{{ 1|db(2,3) }} 管道符前面的参数是第一个参数。

2. 全局定义函数

全局定义函数可以不经过任何视图函数传送即可在前端使用,需要用到 @app.template_global()@app.template_filter() 两个装饰器。

@app.template_global()
def sb(a1, a2):
    # {{sb(1,9)}}
    return a1 + a2

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

7.3 模板继承

在 Flask 中,模板的继承与 Django 中的使用方法一致。

1. extends

layout.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <h1>模板</h1>
    {% block content %}{% endblock %}
</body>
</html>

tpl.html

{% extends "layout.html"%}


{% block content %}
    {{users.0}}


{% endblock %}  

2. include

form.html

<form>
    asdfasdf
    asdfasdf
    asdf
    asdf
</form>

在 xxx.html 调用:{% include "form.html" %}

7.4 宏

Flask 中的宏相当于将某些有特征功能的 HTML 代码封装起来,并能进行传值操作,使之在前端能适应不同场景的应用。

定义:

{% macro ccccc(name, type='text', value='') %}
    <h1>宏</h1>
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    <input type="submit" value="提交">
{% endmacro %}

调用:

{{ ccccc('n1') }}

{{ ccccc('n2') }}

8 Session

当请求刚到来:Flask 读取 cookie 中 session 对应的值(如eyJrMiI6NDU2LCJ1c2VyIjoib2xkYm95),将该值解密并反序列化成字典,放入内存以便视图函数使用。

设置 session:session['k1'] = 123
删除 session:del session['k1']

当请求结束时,Flask 会读取内存中字典的值,进行序列化 + 加密,写入到用户 cookie 中。

9 闪现

闪现可以理解为一次性的 session ,只要有一次请求获取该值,则该值就会被删除。原理为,在 session 中存储一个数据,读取时通过 pop 将数据移除。

通过 flash 函数来添加闪现,并且可以指定闪现的类型。flash('临时数据存储','error')

通过 get_flashed_messages 来获取闪现中的值,在该函数中可以指定 category_filter 字段来过滤闪现类型,获取自己需要类型的闪现。

from flask import Flask,flash,get_flashed_messages
@app.route('/page1')
def page1():

    flash('临时数据存储','error')
    flash('sdfsdf234234','error')
    flash('adasdfasdf','info')

    return "Session"

@app.route('/page2')
def page2():
    print(get_flashed_messages(category_filter=['error']))
    return "Session"

10 中间件

Flask 也支持与 Django 类似的中间件功能。

通过查看源码可以得知,Flask 中间件功能类似于 flask 类中的 call 方法,而实现中间件则是重写 call 方法即可。

而 call 方法在用户发起请求时,才执行。

from flask import Flask

app = Flask(__name__)


@app.route('/index')
def index():
    print('index')
    return "Index"


class Middleware(object):
    def __init__(self,old):
        self.old = old

    def __call__(self, *args, **kwargs):
        ret = self.old(*args, **kwargs)
        return ret


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

11 特殊装饰器

  1. before_request

  2. after_request

示例:

from flask import Flask
app = Flask(__name__)


@app.before_request
def x1():
    print('before:x1')
    return '滚'

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


@app.after_request
def x2(response):
    print('after:x2')
    return response

@app.after_request
def xx2(response):
    print('after:xx2')
    return response



@app.route('/index')
def index():
    print('index')
    return "Index"


@app.route('/order')
def order():
    print('order')
    return "order"


if __name__ == '__main__':

    app.run()
  1. before_first_request
from flask import Flask
app = Flask(__name__)

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


@app.route('/index')
def index():
    print('index')
    return "Index"


@app.route('/order')
def order():
    print('order')
    return "order"


if __name__ == '__main__':

    app.run()
  1. template_global

  2. template_filter

  3. errorhandler

出现错误时返回的内容。

@app.errorhandler(404)
def not_found(arg):
    print(arg)
    return "没找到"

701977-20190501094902261-873549745.png

转载于:https://www.cnblogs.com/banshaohuan/p/10346346.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值