Blog icon indicating copy to clipboard operation
Blog copied to clipboard

Flask 学习

Open codcodog opened this issue 7 years ago • 0 comments

Flask 学习

目录

  • 项目结构
    • 项目的目录结构是如何定义的
    • 是否具备入口文件
  • 定义路由
    • 如何定义一个URL
  • Request & Response
    • 获取Request参数(GET, POST)
    • 定义Url拦截函数(请求钩子)
    • 表单验证(数据过滤)
    • 获取/修改/存储Cookie和Session
    • 请求获取第三方API数据, 即通过Http Get/Post 获取远程数据
    • 修改/输出 Http Header数据
    • 解释/输出 Json数据
    • 如何处理状态码: 404 和 50X
  • 模板系统
    • 如何组织/访问模板文件的目录结构
    • 如何在模板中嵌入代码
    • 模板是否支持继承结构
    • 模板之间如何include
    • 如何自定义模板函数
  • 数据库操作
    • 是否支持ORM
    • 如何直接使用sql语句访问数据库(而不用orm)
    • 如何定义/组织/初始化 数据表
    • 掌握orm常用操作, 例如: add/delete/字段查询/counts/order by等
  • 非框架必备技能(Helpers)
    • 文件上传
    • Log日志
    • 发送Email
    • 图片处理

项目结构

项目的目录结构是如何定义的

由于Flask把代码的组织职能委托给用户, 所以项目的目录结构一般是用户根据自己的需要而实现.

大型程序的目录结构(参考)

|-flasky
 |-app/
  |-templates/
  |-static/
  |-main/
   |-__init__.py
   |-errors.py
   |-forms.py
   |-views.py
  |-__init__.py
  |-email.py
  |-models.py
 |-tests/
  |-__init__.py
  |-test*.py
 |-venv/
 |-requirements.txt
 |-config.py
 |-manage.py

说明:

  • Flask程序一般都保存在app包中

  • 单元测试编写在tests包中

  • venv文件夹包含Python虚拟环境

  • templates存储视图文件(html)

  • static存储静态文件(css, js等)

  • main存储Flask程序

  • requirements.txt列出了所有的依赖包, 便于在其他电脑中重新生成相同的虚拟环境.

    pip可以使用如下命令自动生成这个文件:

    (venv) $ pip freeze > requirements.txt
    

    如果你要创建这个虚拟环境的完全副本, 可以创建一个新的虚拟环境, 并在其上运行以下命令:

    (venv) $ pip install -r requirements.txt
    
  • config.py存储配置设置.

  • manage.py用于启动程序以及其他的程序任务.

是否具备入口文件

一般Web框架都会具备一个统一的入口文件, 例如: PHP Web框架一般是index.php

在入口文件, 初始化框架的时候我们可以加载一系列框架的功能函数, 或者对框架进行一些预加载, 缓存, 钩子等操作.

通常, 路由的映射也是在此阶段完成的.

由于Flask的高可定制性(项目的代码结构组织职能委托给用户), 这个入口文件是否具备也是用户决定

一般我们会选择__init__.py作为入口文件, 当然也可以自定义其他文件作为入口文件.

定义路由

处理URL和函数之间关系的程序称为路由.

如何定义一个URL

  • 利用route()装饰器, 把装饰的函数注册为路由.
@app.route('/')
def index():
    return 'Index Page'
  • 动态路由

规则: <converter:variable_name>

@app.route('/user/<username>')
def show_user_profile(username):
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return 'Post %d' % post_id

转换器:

int: 接受整数
float: 同int, 但是接受浮点数
path: 和默认的相似, 但也接受斜线

  • 唯一URL / 重定向行为

Flask的URL 规则基于Werkzeug的路由模块. 这个模块背后的思想是基于Apache以及更早的HTTP服务器主张的先例,保证优雅且唯一的URl.

@app.route('/project/')
def project():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

虽然它们看起来相似, 但注意它们结尾斜线的使用在URL定义中不同.

第一种情况, 指向project的规范URL尾端有一个斜线. 这种感觉很像在文件系统中的文件夹.
访问一个结尾不带斜线的URL会被Flask重定向到带斜线的规范URL中去.

然而, 第二种情况的URL结尾不带斜线, 类似 UNIX-like 系统下的文件的路径名. 访问结尾带斜线的URL, 会 产生一个404 "Not Found" 的错误.

这个行为使得在遗忘尾斜线时, 允许关联的URL接任工作, 与Apache和其他服务器的行为并无二异. 此外, 也保证了 URL的唯一, 有助于避免搜索引擎引同一个页面两次.

  • 构造URL

使用 url_for() 来给指定的函数构造URL.

>>> with app.test_request_context():
...  print url_for('index')
...  print url_for('profile', username='John Doe')
...
/
/user/John%20Doe
  • HTTP 方法
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()
  • 蓝图(blueprints)

Flask 用 蓝图 的概念来在一个应用中或跨应用制作应用组件和支持通用的模式.
一个Blueprint对象与Flask应用对象的工作方式很像, 但它确实不是一个应用, 而是一个描述如何构建或扩建应用的蓝图.
蓝图中定义的路由处于休眠状态, 直到蓝图注册到程序上后, 路由才真正成为程序的一部分.

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

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

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

注册蓝图:

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

详细了解:

用蓝图实现模块化的应用
sopython.com

(PS: 查看sopython.com项目源码, 可以了解到大型项目中如何使用蓝图构建应用)

Request & Response

Flask的请求对象request

详细了解官方API: request

获取请求参数

  • 获取GET请求数据

    request.args.get('key')
    
  • 获取表单数据(POST请求)

    request.form.get('key', tyep=str, default=None)
    
  • 获取所有参数(GET & POST)

    request.values.get('key')
    
  • 文件上传

    request.files['the_file']
    
  • 流(stream)

    request.stream()
    request.data()
    

定义Url拦截函数

请求钩子使用装饰器实现
  • before_first_request

注册一个函数, 在处理第一个请求之前运行.

  • before_request

注册一个函数, 在每次请求之前运行

  • after_request

注册一个函数, 如果没有未处理的异常抛出, 在每次请求之后运行.

  • teardown_request

注册一个函数, 即使有未处理的异常抛出, 也在每次请求之后运行.

表单验证

使用 WTForms 进行表单验证.(Flask-WTF 扩展)

具体使用, 不再赘述. 详情查看: WTForms

另外, 可以参考: 使用 WTForms 进行表单验证

如何修改cookie和session

通过cookies属性来访问Cookies, 用响应对象的set_cookie方法来设置cookie.

  • 读取Cookie

    from flask import request
    
    @app.route('/')
    def index():
        username = request.cookies.get('username')
    
  • 存储Cookie

    from flask import make_response
    
    @app.route('/')
    def index():
        resp = make_response(render_template(...))
        resp.set_cookie('username', 'the username')
        return resp
    
  • 会话(Session)

如果你设置了 Flask.secret_key ,你可以在 Flask 应用中使用会话(设置app.secret_key).
会话对象很像通常的字典,区别是会话对象会追踪修改。
flask.session 对象.

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))

请求获取第三方api数据

使用requests库, 进行第三方数据的请求.
详细参考: requests

Http header数据

使用Flask.request.headerFlask.response.header 对http headers进行获取, 修改.

  • 获取 Header
from flask import request

request.headers.get('your-header-name')
  • 修改, 输出 Header
@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

(PS: 可以参考 flask.Response, flask.make_response())

json数据

使用 json.dumps(), json.loads() 进行json数据处理

  • 输出json数据
@app.route('/json', methods=['POST'])
def my_json():
    rt = {'info':'hello '+request.json['name']}
    return Response(json.dumps(rt),  mimetype='application/json')

处理错误页

  • 重定向
@app.route('/')
def index():
    return redirect(url_for('login'))
  • 放弃请求并返回错误代码,用 abort() 函数
@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()
  • 处理错误页
@app.errorhandler(403)
def forbidden(e):
    return render_template('errors/403.html'), 403

@app.errorhandler(404)
def not_found(e):
    return render_template('errors/404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('errors/500.html'), 500

模板系统

模板渲染使用 render_template()函数
并且, Flask配备了 Jinja2 模板引擎.

详细参考: Jinja2

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

数据库操作

Flask ORM 操作: SQLAlchemy

详细参考: SQLAlchemy

非框架必备技能(Helpers)

Flask 扩展用多种不同的方式扩充 Flask 的功能.

寻找扩展: Flask Extension Registry

codcodog avatar Aug 10 '17 08:08 codcodog