python flask框架(上)

flask文档
flask最佳实践

已经解释的非常详细了,我这里只把我关注的点以我的理解记录一下。

一:程序的基本结构

1. 初始化
2. 路由和视图函数
  • flask使用route装饰器把一个函数绑定到对应的 URL 上,把修饰的函数注册为路由
  • URL 使用尖括号 <variable_name> , flask将这个部分作为命名参数传递到你的函数。可以给该参数定义转换器实现类型转换。
    转换器又:int,float,path
@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
3. 程序和请求的上下文
  • Flask使用在 Flask 中由全局的 request 对象来提供这些信息客户端交互信息。

  • Flask 从客户端收到请求时,要先让视图函数能访问一些对象,这样才能处理请求。例如request对象封装了客户端发送的 HTTP 请求。

  • Flask 使用上下文临时把某些对象变为全局可访问。

  • 在 Flask 中有两种上下文:程序上下文和请求上下文


    flask上下文
  • Flask 在分发请求之前激活(或推送)程序和请求上下文,请求处理完成后再将其删除

  • 在程序实例上调用 app.app_context() 可获得一个程序上 下文。

  • Flask 中的某些对象是全局对象,但却不是通常的那种。这些对象实际上是特定环境的局部对象的代理。虽然很拗口,但实际上很容易理解。

  • 想象一下处理线程的环境。一个请求传入,Web 服务器决定生成一个新线程( 或者别的什么东西,只要这个底层的对象可以胜任并发系统,而不仅仅是线程)。 当 Flask 开始它内部的请求处理时,它认定当前线程是活动的环境,并绑定当前的应用和 WSGI 环境到那个环境上(线程)。它的实现很巧妙,能保证一个应用调用另一个应用时不会出现问题。

  • 所以,这对你来说意味着什么?除非你要做类似单元测试的东西,否则你基本上可以完全无视它。你会发现依赖于一段请求对象的代码,因没有请求对象无法正常运行。解决方案是,自行创建一个请求对象并且把它绑定到环境中。单元测试的最简单的解决方案是:用 <tt class="xref py py-meth docutils literal" style="font-family: Consolas, Menlo, "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 0.9em; background-color: rgb(251, 251, 251); color: rgb(34, 34, 34); font-weight: bold; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: white;">test_request_context()</tt> 环境管理器。结合 <cite>with</cite> 声明,绑定一个测试请求,这样你才能与之交互

4. 请求调度
  • 程序收到客户端发来的请求时,要找到处理该请求的视图函数。为了完成这个任务,Flask 会在程序的 URL 映射中查找请求的 URL。URL 映射是 URL 和视图函数之间的对应关系。 Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。
  • URL 映射中的 HEAD、Options、GET 是请求方法,由路由进行处理。Flask 为每个路由都指 定了请求方法,这样不同的请求方法发送到相同的 URL 上时,会使用不同的视图函数进 行处理。
5. 请求钩子
  • 有时需要在处理请求之前或之后执行代码。例如,认证发起请求的用户。为了避免在每个视图函数中都使用重复的代码, Flask 提供了注册通用函数的功能,注册的函数可在请求被分发到视图函数之前或之后调用。这种程序叫做钩子函数。
  • 请求钩子使用修饰器实现
  • flask支持以下几种钩子
    • before_first_request:注册一个函数,在处理第一个请求之前运行
    • before_request:注册一个函数,在每次请求之前运行
    • after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行
    • teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
  • 在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g,例如,before_ request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视 图函数时,视图函数再使用 g.user 获取用户。
6. 响应
  • Flask 视图函数一般返回一个 Response 对象。make_response() 函数可接受 1 个、2 个或 3 个参数(和视图函数的返回值一样),并返回一个 Response 对象。
from flask import make_response
     @app.route('/')
     def index():
         response = make_response('<h1>This document carries a cookie!</h1>')
         response.set_cookie('answer', '42')
         return response
  • 重定向是一种特殊的响应,响应内容是 URL,而不是包含 HTML 代码的字符串。浏览器收到 这种响应时,会向重定向的 URL 发起 GET 请求,显示页面的内容。
  • 重定向响应可以使用 3 个值形式的返回值生成,也可在 Response 对象中设定。不过,由于使用频繁,Flask 提 供了 redirect() 辅助函数,
from flask import redirect
     @app.route('/')
     def index():
return redirect('http://www.example.com')
  • 可以使用redirect() 辅助函数, 生成 HTTP 重定向响应。redirect() 函数的参数是重定向的 URL,推荐使用 url_for() 生成 URL,因为这 个函数使用 URL 映射生成 URL,从而保证 URL 和定义的路由兼容,而且修改路由名字后 依然可用。
  • url_for() 函数的第一个且唯一必须指定的参数是端点名,即路由的内部名字。默认情 况下,路由的端点是相应视图函数的名字。
  • 重定向时请求数据会request上下文的内容会丢失,因为重定向相当于另一个请求,这时程序可以把数据存储在session中,在请求之间“记住”数据。session是一种私有存 储,存在于每个连接到服务器的客户端中。默认情况下,session保存在客户端 cookie 中
from flask import Flask, render_template, session, redirect, url_for
     @app.route('/', methods=['GET', 'POST'])
     def index():
         form = NameForm()
         if form.validate_on_submit():
             session['name'] = form.name.data
             return redirect(url_for('index'))
         return render_template('index.html', form=form, name=session.get('name'))

flask扩展

社区成员开发了大量不同用途的flask扩展,下面介绍如何把扩展整合到程序中

  1. 使用pip安装flask扩展
  2. 在程序中使用from flask.ext.script import Manager flask.ext模块导入该扩展
  • 专为 Flask 开发的扩展都暴漏在 flask.ext 命名空间下

二:模版

  • 默认情况下,Flask 在程序文件夹中的 templates 子文件夹中寻找模板
  • flask支持很多模版引擎, render_template 函数把 Jinja2模板引擎集成到了程序中
  • render_template 函 数的第一个参数是模板的文件名。随后的参数都是键值对,表示模板中变量对应的真实值
from flask import Flask, render_template # ...
     @app.route('/user/<name>')
     def user(name):
         return render_template('user.html', name=name)
1. 变量
  • 在模板中使用的{{ name }}结构表示一个变量,它是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取
  • 可以使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔
  • Jinja2 提供的部分常用过滤器:


    Jinja2 提供的部分常用过滤器
2. jinja2控制结构
  • Jinja2 提供了多种控制结构,可用来改变模板的渲染流程
条件控制语句
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}

for循环
<ul>
{% for comment in comments %}
<li>{{ comment }}</li> {% endfor %}
</ul>

引入别的文件
{% include 'common.html' %}
  • 需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免 重复:
  • 模版继承
    base.html
   <html>
     <head>
{% block head %}  //声明一个代码块
<title>{% block title %}{% endblock %} - My Application</title> {% endblock %}
     </head>
     <body>
{% block body %}
{% endblock %} </body>
</html>
{% extends "base.html" %}  声明这个模板衍生自 base.html。
{% block title %}Index{% endblock %} {% block head %}   //覆盖父类代码块
         {{ super() }} 
         <style>
         </style>
{% endblock %}
{% block body %} <h1>Hello, World!</h1> {% endblock %}
推荐使用flask-bootstrap框架
  • Flask-Bootstrap 中的基模板提供了一个网页框架,引入了 Bootstrap 中的所有 CSS 和JavaScript 文件。
3. 自定义错误显示页面
  • Flask 允许程序使用基于模板的自定义错误页面
4.链接
  • Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存 的信息生成 URL
  • url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用 的端点名)作为参数,返回对应的 URL。
  • 使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如,url_for ('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john。。传入 url_for() 的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到 查询字符串中,例如,url_for('index', page=2) 的返回结果是 /?page=2。
5.静态文件
  • 默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon"> {% endblock %}

四:web表单

  • request.form 能获取 POST 请求中提交的表单数据。
  • Flask-WTF扩展可以把处理 Web 表单的过程变成一 种愉悦的体验。
1. 跨站请求伪造保护
  • 默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(CSRF)的攻击
  • 为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
  • app.config 字典可用来存储框架、扩展和程序本身的配置变量。
4.2 表单类
  • 类定义表单中的 一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来 验证用户提交的输入值是否符合要求。
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()]) submit = SubmitField('Submit')

//字段构造函数的第一个参数是把表单渲染成 HTML 时使用的标号
//StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受 用户提交的数据之前验证数据。验证函数 Required() 确保提交的字段不为空
3.把表单渲染成HTML
  • 表单字段是可调用的,在模板中调用后会渲染成 HTML。
  • Flask-Bootstrap 提供了一个非常高端的辅助函 数,可以使用 Bootstrap 中预先定义好的表单样式渲染整个 Flask-WTF 表单
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %} <div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div>
{{ wtf.quick_form(form) }} {% endblock %}
  1. 在视图函数中处理表单
@app.route('/', methods=['GET', 'POST'])
     def index():
         name = None
         form = NameForm() 在视图函数中创建一个 NameForm 类实例用于表示表单
         if form.validate_on_submit():
             name = form.name.data
             form.name.data = ''
         return render_template('index.html', form=form, name=name)
  1. Flash消息
  • flash() 函数

七:大型程序的结构

import os


basedir = os.path.abspath(os.path.dirname(__file__))
class Config:  #基类 Config 中包含通用配置,子类分别定义专用的配置。
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' 
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
    FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>' 
    FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
    @staticmethod
    def init_app(app):
          pass


class DevelopmentConfig(Config):
    DEBUG = True
    MAIL_SERVER = 'smtp.googlemail.com'
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
      'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')


class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
              'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
              'sqlite:///' + os.path.join(basedir, 'data.sqlite')


config = {
'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig,
'default': DevelopmentConfig }
  • 为了让配置方式更灵活且更安全,某些配置可以从环境变量中导入。例如,SECRET_KEY 的值, 这是个敏感信息,可以在环境中设定,但系统也提供了一个默认值,以防环境中没有定义。
  • 配置类可以定义 init_app() 类方法,其参数是程序实例。
2. 蓝本blueprint
  • 蓝本和程序类似,也可以定义路由。不同的 是,在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序 的一部分。使用位于全局作用域中的蓝本时,定义路由的方法几乎和单脚本程序一样
  • 蓝本可以在单个文件中定义,也可使用更结构化的方式在包中的多个模块中创建
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
  • 程序的路由保存在包里的 app/main/views.py 模块中,而错误处理程序保存在 app/main/ errors.py 模块中。导入这两个模块就能把路由和错误处理程序与蓝本关联起来

参考链接:
Flask 文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容