python3 flask框架

一. 安装

推荐使用 python 3 , 但 Flask 支持 Python 3.4+Python2.7PyPy

依赖

下列的包会在安装 Flask 时自动安装:

  • Werkzeug: WSGI 的实现
  • Jinja: 模板渲染语言.
  • MarkupSafe: Jinja提供的, 目的是为了渲染的安全, 防止XSS攻击等
  • ItsDangerous: 安全地签署数据以确保其完整性。 这用于保护Flask的会话cookie。
  • Click: 编写命令行工具的框架. 它为 Flask 提供命令,并且允许添加自定义的管理命令.

还有一些可选的依赖, Flask 会检测它们是否安装,如果安装了则使用它们:

  • Blinker: 提供了对 Signals 的支持
  • SimpleJSON:
  • python-dotenv: 运行 flask 命令时,可以从 .env.flaskenv 中读取环境变量.
  • Watchdog: 为开发时使用的server 提供了更快速的reloader

虚拟环境

mkdir flaskyProject
cd flaskyProject

# 创建虚拟环境
python3 -m venv venv
# 激活虚拟环境
source ven/bin/activate 激活
# 或者在windows 下直接使用 ven/Scripts/activate 激活

# 以上也可以直接使用 pycharm 创建项目, pycharm 会自动创建虚拟环境, 然后在 pycharm 的 Terminal 中执行命令

# 安装 flask
pip3 install Flask

# 安装其他常用的包


二. 快速开始

最简单的例子

创建一个文件, 名称为 hello.py , 内容如下:
(注意不能叫 flask.py )

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

运行它

export FLASK_APP=hello.py
# 或者在Windows下  set FLASK_APP=hello.py
flask run

# 设置调试模式
export FLASK_ENV=development
# 也可以配置单独的环境变量
export FLASK_DEBUG=1


flask run --host=0.0.0.0

这只是启动了一个非常简单的 内置 server ,用来测试是足够了,
但是你可能不会想在 生产环境 使用它.

现在打开浏览器访问 http://127.0.0.1:5000/

from flask import Flask,request,make_response,redirect,render_template,abort

app=Flask(__name__)

@app.route('/', methods=['GET','POST'])
def index():
    return 'Hello World!', 200

# 动态路由
@app.route('/user/<name>', methods=['GET','POST'])
def user(name):
    return 'Hello {}!'.format(name), 200

# 动态路由支持变量类型
# 包括 string(默认选项,不支持/),int,float,path(和string类似,但允许/),uuid
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % subpath

# 路由声明为 /projects/ (以/结尾)时, 当访问 /project 时会重定向到 /projects/  
# 但反之不可以
# 即路由声明为 /projects 时, 不可以匹配 /project/ 
@app.route('/projects/')
def projects():
    return 'The project page'


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

url_for

from flask import Flask, url_for

app = Flask(__name__)

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

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

@app.route('/user/<username>')
def profile(username):
    return '{}\'s profile'.format(username)

with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))

# 输出
"""
/
/login
/login?next=/
/user/John%20Doe
"""

request 对象

from flask import Flask, request, make_response

app = Flask(__name__)


@app.route('/', methods=['POST', 'GET'])
def hello_world():
    username = None
    if request.method == 'POST':
        try:
            # 当form中的key不存在时会抛出 KeyError异常
            username = request.form['username']
        except KeyError as e:
            pass
    else:
        username = request.args.get('username', '')

    # 获取所有参数
    all_params_dict = request.values.to_dict()

    if username and username != '':
        return 'Hello, {}!'.format(username)
    else:
        return 'Hello, World!'

# 上传文件
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
        # 当你使用表单中提供的filename时要注意使用secure_filename
        f.save('/var/www/uploads/' + secure_filename(f.filename))

# 读取cookie
@app.route('/cookie')
def get_cookies():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

# 设置cookie
@app.route('/setcookie')
def set_cookies():
    resp = make_response('this page is for set cookie')
    resp.set_cookie('username', 'the username')
    return resp

# redirect
@app.route('/redirect')
def redirect_url():
    return redirect(url_for('hello_world'))

# error
@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

# errorHandler
@app.errorhandler(404)
def page_not_found(error):
    return 'page_not_found', 404

response 对象

from flask import Flask, request, make_response

app = Flask(__name__)

@app.errorhandler(404)
def not_found(error):
    resp = make_response('page_not_found', 404)
    resp.headers['X-Something'] = 'A value'
    return resp

session 对象

通过cookie中的 session 这个key实现, 其内容时加密的.
然后 session 中的内容完全保存在 cookie 中, 所以当session中的内容过多时要注意浏览器对cookie的长度限制.

session['username'] = 'llllllll'
session.pop('username', None)

但是这个 session 机制还是有点问题的, 客户端如果保存了这个 session 后, 服务端何时认定这个session过期?

logging

from flask import Flask, request, make_response

app = Flask(__name__)

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')


三. 测试 Flask 应用

http://flask.pocoo.org/docs/1.0/testing/

测试工具 pytest

pip3 install pytest

测试骨架

假设我们的程序是 hello.py, 其内容如下:

from flask import Flask 

app = Flask(__name__)

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

测试骨架:

# pytest 会自动检测 test_*.py 文件 中的 test_* 方法
cat > test/test_hello.py << EOF
import pytest
import hello


@pytest.fixture
def client():
    yield hello.app.test_client()

# pytest 会自动检测 test_*.py 文件 中的 test_* 方法
def test_index(client):
    rv = client.get('/', follow_redirects=True)
    print(rv.data)
    assert b'hello world!' in rv.data
    assert b'hello world!' == rv.data


EOF

# 执行测试
pytest -v


四. 处理错误

http://flask.pocoo.org/docs/1.0/errorhandling/

1. 错误日志工具

推荐使用 Sentry , 在应用发生错误的时候给你发邮件.

pip install raven[flask]

五. Logging

http://flask.pocoo.org/docs/1.0/logging/

基础配置

from logging.config import dictConfig

dictConfig({
    'version': 1,
    'formatters': {'default': {
        'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
    }},
    'handlers': {'wsgi': {
        'class': 'logging.StreamHandler',
        'stream': 'ext://flask.logging.wsgi_errors_stream',
        'formatter': 'default'
    }},
    'root': {
        'level': 'INFO',
        'handlers': ['wsgi']
    }
})

app = Flask(__name__)

发生错误给管理员

import logging
from logging.handlers import SMTPHandler

mail_handler = SMTPHandler(
    mailhost='127.0.0.1',
    fromaddr='[email protected]',
    toaddrs=['[email protected]'],
    subject='Application Error'
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(logging.Formatter(
    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
))

if not app.debug:
    app.logger.addHandler(mail_handler)


六. 配置管理

http://flask.pocoo.org/docs/1.0/config/

Flask 类的实例对象有一个 config 属性可以用来保存配置.
它其实就是 dict 的一个子类

from flask import current_app
# 将 app 的config属性转成dict
print(dict(current_app.config)) 

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

# 一些特定的配置会反应到 flask对象的属性上, 例如
app.testing==app.config['TESTING']

# 同时更新多个配置
app.config.update(
    TESTING=True,
    SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
)

# 设置config内容
app.config.from_object()
# 覆盖默认配置内容
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
app.config.from_pyfile('application.cfg', silent=True)
# 或直接配置系统的环境变量(优先级最高)

七. 信号

http://flask.pocoo.org/docs/1.0/signals/

就是解耦, 发生信号,接收信号


八. The Application Context

http://flask.pocoo.org/docs/1.0/appcontext/

current_app

from flask import current_app
print(dict(current_app.config))
my_app=current_app._get_current_object()
# _get_current_object 的作用是获取本线程的应用实例以作为参数传递给其他线程使用,应用实例就是 _find_app中返回的top.app 

g

from flask import g
# 传统的 attr 方法
hasattr(g,'username')
getattr(g,'username',None)
setattr(g,'username',some_value)

# g 对象提供的方法
# 判断 key 是否在 g 中
print('key' in g)

# 遍历 g
for i in g:
    print('attr_name:', i, 'attr_value:', g.get(i, default=None))

# 获取key的值
g.some_key = 'hhha'
print(g.get('some_key', default=None))  # 输出 hhha
print(g.get('some_key2')) # 输出 None


# 获取key的值并移除
g.some_key = 'hhha'
print(g.pop('some_key', default=None))  # 输出 hhha
print(g.pop('some_key', default=None))  # 输出 None
g.some_key = 'hhha'
print(g.pop('some_key'))  # 输出 hhha
print(g.pop('some_key'))  # 抛出异常

# 设置key的值
g.some_key = 'hhha'
# setdefault的作用是当key存在时获取key的值, 否则为该key设置值并返回设置的值
g.some_key = 'hhha'
print(g.setdefault('some_key', 'hhha2'))  # 输出 hhha
print(g.setdefault('some_key2', 'hhha2')) # 输出 hhha2


九. The Request Context

http://flask.pocoo.org/docs/1.0/reqcontext/


十. 应用模块化 with Blueprints

http://flask.pocoo.org/docs/1.0/blueprints/

大型应用的项目结构一般为:

|-flask_project
  |-requirements.txt
  |-config.py
  |-flasky.py
  |-app/
    |-__init__.py
    |-main/
      |-__init__.py
      |-views.py
  |-tests/
    |-__init__.py
    |-test*.py
  |-venv/

其中 config.py 用于应用的配置,其内容一般如下:

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
    SOME_ENV_NAME = '123'

    @staticmethod
    def init_app():
        pass

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    TESTING = True

class ProductionConfig(Config):
    pass

config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,

    'default': DevelopmentConfig
}

app/__init__.py 用于创建应用的工厂函数:

from flask import  Flask
# 其他 flask扩展
from config import config

# 初始化扩展

def create_app(config_name):
    app=Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app()

    # 初始化app

    # 注册 blueprint
    from .main import main as main_blueprint
    app1.register_blueprint(main_blueprint)

    return app

app/main/__init__.py 用于创建blueprint:

from flask import  Blueprint

main=Blueprint('main',__name__)

from . import views

app/main/views.py 用于定义应用路由:

from . import  main

@main.route('/', methods=['GET','POST'], defaults={'page': 'index'})
@main.route('/<page>', methods=['GET','POST'])
def index():
    return 'hello world!'

最后, 主脚本 flasky.py 的内容:

import os

from app import create_app

app=create_app(os.getenv('FLASK_CONFIG') or 'default')

# other statement...

应用中最好有一个 requirements.txt, 用于记录所有依赖包以及其精确的版本号.

pip freeze > requirements.txt
pip install -r requirements.txt

单元测试可以这样:

...
self.app=create_app('testing')

十一. 命令行接口


十二. 部署

猜你喜欢

转载自blog.csdn.net/weixin_34368949/article/details/87213310