Flask

一、基本使用

安装:

pip3 install flask

基本使用:

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

app = Flask(__name__, template_folder="templates", static_folder="static")
app.secret_key = "abc"

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    username = request.form.get("username")  # 如果是get请求 request.args
    password = request.form.get("password")
    if username == "pd" and password == "123":
        session["user"] = username
        return redirect("/index")
    # return render_template("login.html", error="用户名或密码错误")
    return render_template("login.html", **{"error": "用户名或密码错误"})

@app.route("/index")
def index():
    user = session.get("user")
    if not user:
        return redirect("/login")
    return render_template("index.html")

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

二、配置文件

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
        'DEBUG':                                False,  # 是否开启Debug模式
        'TESTING':                              False,  # 是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),  # session超时时间
        '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,
    }
View Code

配置方式1:

app.config["DEBUG"] = True
# 由于Config对象本质上是字典,所以还可以使用
app.config.update({"DEBUG": "True"})

配置方式2:

新建一个 settings.py 文件,例如:

# 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("配置文件类的路径")

所以,生产环境就用生产环境的配置,开发时就用开发环境的配置。

三、路由系统

@app.route("/index", endpoint="xx")  #  endpoint相当于别名
def index():
    print(url_for("xx"))  # /index  url_for("xx"),反向生成url,默认url_for("函数名")
    return "index"
@app.route("/index/<user>")  # 字符串
@app.route("/index/<int:id>")
@app.route("/index/<float:id>")
@app.route("/index/<path:path>")
@app.route("/login", methods=["GET", "POST"])
# 示例:
@app.route("/index/<user>")
def index(user):
    return user
# 常用路由系统有以上五种,所有的路由系统都是基于以下对应关系来处理:
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

反向生成带参数的URL,怎么做呢?

@app.route("/index/<int:id>")
def index(id):
    print(url_for("index", id=id))  # id=1 生成固定参数的url
    return "index"

CBV

from flask import Flask, views, url_for

app = Flask(__name__)

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

class IndexView(views.View):
    methods = ['GET']
    decorators = [auth, ]

    def dispatch_request(self):
        print('Index')
        return 'Index'

app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint

#

class IndexView(views.MethodView):
    methods = ['GET']
    decorators = [auth, ]

    def get(self):
        return 'Index.GET'

    def post(self):
        return 'Index.POST'

app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint
View Code

@app.route和app.add_url_rule参数

rule                    # URL规则
view_func               # 视图函数名称
defaults=None           # 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
endpoint=None           # 名称,用于反向生成URL,即: url_for('名称')
methods=None            # 允许的请求方式,如:["GET", "POST"]

strict_slashes=None     # 对URL最后的 / 符号是否严格要求,
                        # 如:
                        @app.route('/index',strict_slashes=False),
                            访问 http://www.xx.com/index/ 或 http://www.xx.com/index 均可
                        @app.route('/index',strict_slashes=True)
                            仅访问 http://www.xx.com/index

redirect_to=None,       # 重定向到指定地址
                        # 如:
                        @app.route('/index/<int:id>', redirect_to='/home/<id>')
                        或
                        def func(adapter, id):
                            return "/home/888"
                        @app.route('/index/<int:nid>', redirect_to=func)

subdomain=None,         # 子域名访问
                        from flask import Flask, views, url_for

                        app = Flask(import_name=__name__)
                        app.config['SERVER_NAME'] = 'wupeiqi.com:5000'

                        @app.route("/", subdomain="admin")
                        def static_index():
                            """Flask supports static subdomains
                            This is available at static.your-domain.tld"""
                            return "static.your-domain.tld"

                        @app.route("/dynamic", subdomain="<username>")
                        def username_index(username):
                            """Dynamic subdomains are also supported
                            Try going to user1.your-domain.tld/dynamic"""
                            return username + ".your-domain.tld"

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

自定制正则路由匹配

from flask import Flask, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)


class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        :param value: 
        :return: 
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        :param value: 
        :return: 
        """
        val = super(RegexConverter, self).to_url(value)
        return val


# 添加到flask中
app.url_map.converters["regex"] = RegexConverter


@app.route("/index/<regex('\d+'):id>")
def index(id):
    return url_for("index", id=id)


if __name__ == "__main__":
    app.run()
View Code

四、请求和响应

from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
from flask import jsonify
import os

app = Flask(__name__)

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

@app.route("/login", methods=["GET", "POST"])
def login():
    # 请求相关信息
    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.get("the_file_name")  # 拿到上传的文件名
    obj.save(os.path.join(BASE_DIR, "files", obj.filename))  # 保存到本地路径

    # 响应相关信息(响应体)
    return "字符串"
    data = {"key": "val"}
    return jsonify(data)  # 相当于 json.dumps(data)
    return render_template("login.html", **{})
    return redirect("/index")
    # 设置响应头
    response = make_response(render_template("index.html"))  # 将响应内容封装到这个对象中,再设置
    # response是flask.wrappers.Response类型
    response.delete_cookie("key")
    response.set_cookie("key", "val")
    response.headers["key"] = "val"
    return response

if __name__ == "__main__":
    app.run()
View Code

五、Session

session 对象,允许在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,需要设置一个密钥。

设置:session["user"] = "xx"
获取:session.get("user")
   session["user"] 删除:session.pop(
"user", None) # 可以在settings中设置秘钥 class DevelopmentConfig(Config): DEBUG = True SECRET_KEY = "asdf"

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

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

第三方session

"""
pip3 install redis
pip3 install flask-session
"""

import redis
from flask import Flask, session
from flask_session import Session
# from flask.ext.session import Session  # 老版本,同 from flask_session import Session

app = Flask(__name__)

app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = redis.Redis(host="127.0.0.1",port=6379)
Session(app)

@app.route("/login")
def login():
    session["user"] = "username"
    return "login"

@app.route("/index")
def index():
    print(session.get("user"))
    return "index"

if __name__ == "__main__":
    app.run()
View Code

flash

临时存储数据,取了就没了;在session中存储一个数据,读取时通过pop将数据移除。

from flask import Flask, flash, get_flashed_messages

app = Flask(__name__)

@app.route('/test1')
def test1():
    flash('aa', 'error')  # 第二个参数:分类
    flash('bb', 'error')
    flash('cc', 'info')
    return "Set Session"

@app.route('/test2')
def test2():
    print(get_flashed_messages(category_filter=['error']))  # ['aaaaa', 'bbbbb']
    return "Get Session"

if __name__ == "__main__":
    app.run()
View Code

六、模板

1、Flask 使用的是 Jinja2 模板,所以其语法和 Django 无差别。

{% for k,v in data.items() %}
<tr>
    <td>{{ k }}</td>
    <td>{{ v.name }}</td>
    <td>{{ v["age"] }}</td>
    <td>{{ v.get("hobby", "默认") }}</td>
</tr>
{% endfor %}
@app.route("/test")
def test():
    txt1 = Markup("<h1>哈哈</h1>")  # Markup相当于Django的mark_safe
    txt2 = "<h1>呵呵</h1>"
    return render_template("test.html", **{"txt1": txt1, "txt2": txt2})
#######################################################################
{{ txt1 }}
{{ txt2|safe }}
@app.template_global()
def func1(a1, a2):
    return a1+a2

@app.template_filter()
def func2(a1, a2, a3):
    return a1+a2+a3

# 调用方式,模板中:{{ func1(1,2) }}  {{ 1|func2(2,3) }}

2、自定义模板方法

from flask import Flask, render_template

app = Flask(__name__)

def func():
    return '<h1>哈哈哈</h1>'

@app.route('/test')
def test():
    return render_template('test.html', xx=func)

if __name__ == "__main__":
    app.run()
################################################
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>自定义函数</h1>
    {{ xx()|safe }}
</body>
</html>
View Code

macro

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{# 定义 #}
{% macro func(name, type='text', value='') %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{# 执行 #}
{{ func('password', type='password', value=123456) }}
</body>
</html>
View Code

七、特殊装饰器

from flask import Flask

app = Flask(__name__)

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

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

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

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

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

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

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

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

# before_first_request1
# before_first_request2
# before_request1
# before_request2
# after_request2 <Response 5 bytes [200 OK]>
# after_request1 <Response 5 bytes [200 OK]>

############################################

# 比较少的函数中需要额外添加功能,使用装饰器
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 'index'
    
# 给全局加上登录验证
@app.before_request
def before_request():
    if request.path == "/login":
        return None
    if session.get("user"):
        return None
    return redirect("login")

############################################

@app.errorhandler(404)
def page_not_found(error):
    return '页面不见了'
View Code

八、蓝图

蓝图用于为应用提供目录划分。

小中型:

account.py

from flask import Blueprint
from flask import render_template

account = Blueprint("account", __name__)

@account.route("/login", methods=["GET", "POST"])
def login():
    return render_template("login.html")

course.py

from flask import Blueprint

course = Blueprint("course", __name__)

__init__.py

from flask import Flask
from .views.account import account
from .views.course import course

app = Flask(__name__, template_folder="templates", static_folder="statics", static_url_path="/static")

app.register_blueprint(account)
app.register_blueprint(course)

run.py

from pro_flask import app

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

大型:

admin / __init__.py

from flask import Blueprint

admin = Blueprint(
    "admin",
    __name__,
    template_folder="templates",
    static_folder="static"
)
from . import views

admin / views.py

from . import admin

@admin.route("/index")
def index():
    return "Admin.Index"

blog / __init__.py

from flask import Blueprint

blog = Blueprint(
    "blog",
    __name__,
    template_folder="templates",
    static_folder="static"
)
from . import view

blog / views.py

from . import blog

@blog.route("/index")
def index():
    return "Blog.Index"

pro_flask / __init__.py

from flask import Flask
from .admin import admin
from .blog import blog

app = Flask(__name__)
app.debug = True

app.register_blueprint(admin, url_prefix="/api")  # url前缀
app.register_blueprint(blog)

run.py

from pro_flask import app

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

url_prefix参数的作用:

http://127.0.0.1:5000/index      # Blog.Index
http://127.0.0.1:5000/api/index  # Admin.Index

小中型应用程序目录:查看

大型应用程序目录:查看

九、Flask插件

http://flask.pocoo.org/extensions/

posted @ 2019-01-20 15:54  就俗人一个  阅读(274)  评论(0编辑  收藏  举报