flask核心知识

栏目: Python · 发布时间: 5年前

内容简介:repo:flask是一个Python语言开发的web“微框架”,和django不同的是,它既没有数据库、也没有表单验证等工具它本身仅仅提供了一个WSGI的桥梁,其他东西统统靠你来定制,具有很大的灵活性为了迅速搭建一个像样的flask网站,我们可以使用脚手架

repo: github.com/alphardex/p…

flask是一个 Python 语言开发的web“微框架”,和django不同的是,它既没有数据库、也没有表单验证等 工具 它本身仅仅提供了一个WSGI的桥梁,其他东西统统靠你来定制,具有很大的灵活性

脚手架

为了迅速搭建一个像样的flask网站,我们可以使用脚手架

之前在Github上看到cookiecutter-flask,是个不错的选择,但是新手可能会看不懂里面代码是如何封装的

于是本人做出了一个更user-friendly的脚手架—— cookiecutter-flask-bootstrap

这个脚手架的功能大致和上个脚手架差不多,不过更加轻量化,而且结构更加清晰明了,best practice也基本都做到了,希望大家用的开心:d

最后还要感谢李辉大大的狼书,给了我很大的帮助

路由

路由是将特定的业务代码(即视图函数)绑定到某个url上,以实现某个功能

注册

flask中使用装饰器来注册路由

@app.route('/')
def index():
    return 'Hello world.'
复制代码

可以为路由传递变量,变量左边可以带转换器用来转换变量的类型

@app.route('/user/<string:username>')
def user_profile(username):
    return f'User {username}'
复制代码

常用的转换器有6种:string, int, float, path, any, uuid

比较特殊的是any,格式如下(var变量只接受a, b其中的任意一值)

@app.route('/<any(a, b):var>/')
复制代码

如果想通过路由直接访问文件呢?用path转换器和send_from_directory就行了

@app.route('/uploads/<path:filename>')
def get_image(filename):
    return send_from_directory(current_app.config['UPLOAD_PATH'], filename)
复制代码

构造url

使用url_for函数可以反向构建访问路由的url

url_for('index')  # '/'
url_for('user_profile', username='alphardex')  # '/user/alphardex'
url_for('index', _external=True)  # 'http://localhost:5000/' 绝对路径
复制代码

HTTP

路由默认支持GET方法,如果需要POST方法则需在route的methods中传入

HTTP methods的用处如下:

  • GET:获取资源
  • POST:创建资源
  • PUT:更新资源
  • DELETE:删除资源
@app.route('/login', methods=['GET', 'POST'])
复制代码

如果想更改HTTP请求头内容,那就要用到make_response

比如制作网站的RSS,就需要把响应的mimetype设置为application/xml

@app.route('/rss')
def rss():
    articles = Article.query.order_by(Article.date.desc).limit(10)
    rss = render_template('rss.xml', articles=articles)
    response = make_response(rss)
    response.mimetype = 'application/xml'
    return response
复制代码

模板

渲染一个模板,简言之就是通过上下文变量来生成HTML

from flask import render_template

@app.route('/')
def index():
    greetings = 'Hello world.'
    return render_template('index.html', greetings=greetings)
复制代码

render_template中第一个参数是要渲染的模板文件名, 其余参数则是上下文变量

通过mustache语法将上下文变量传入模板并渲染,同时也支持if、for等控制流语句语法,更高级的有过滤器、模板继承、宏等

提几个常用的过滤器:

  • safe: 避免HTML的自动转义,本质上是个Markup对象
  • length: 获取变量长度
  • default: 为变量设置默认值
  • trim: 去除变量前后的空格
  • tojson: 将变量转化为json
  • truncate: 截断字符串,常用于显示文章摘要

网站的静态文件放在static文件夹中,通过反向构造url访问

url_for('static', filename='style.css')
复制代码

上下文全局变量

  • current_app:指向处理请求的app实例
  • g:global的简写,以object的方式存储信息(比如用户登录后的用户对象 g.user)
  • request:以dict形式存储HTTP请求相关变量
  • session:以dict的方式存储会话信息(比如用户登录后的用户id session['user_id'])

以下是request所封装的几个最常用的参数,全部参数请点这里

request.args        # GET请求的查询字符串
request.cookies     # cookies信息
request.files       # 请求上传的文件
request.form        # POST请求的表单数据
request.headers     # 请求头
request.method      # 请求类型
request.path        # 请求路径
request.referrer    # 请求发源地址
request.remote_addr # 用户的ip地址
request.get_json()  # 获取api的json数据
复制代码

工具函数

  • abort:放弃请求
  • flash:闪现信息,可以附带类别
  • jsonify:将数据序列化为json,常用于设计restful api
  • redirect:重定向

工厂模式

工厂模式使得app在创建之时能同时完成以下步骤:加载配置,初始化扩展,注册蓝本,注册 shell 上下文,以及注册错误处理函数等

def create_app(config_name=None):
    if config_name is None:
        config_name = os.getenv('FLASK_CONFIG', 'development')

    app = Flask(__name__)
    app.config.from_object(config[config_name])

    register_blueprints(app)
    register_extensions(app)
    register_shell_context(app)
    register_errors(app)

    return app

def register_extensions(app):
    debugtoolbar.init_app(app)
    ...

def register_blueprints(app):
    app.register_blueprint(main_bp)
    ...

def register_shell_context(app):
    @app.shell_context_processor
    def make_shell_context():
        return {'db': db, ...}

def register_errors(app):
    @app.errorhandler(400)
    def bad_request(e):
        return render_template('errors/400.html'), 400
    ...
复制代码

蓝本

用来实现模块化编程

例如一个app(名字叫flasky)通常会有以下的文件夹结构

├─flasky
│  ├─blueprints
│  ├─static
│  │  └─css
│  └─templates
│      ├─auth
│      ├─errors
│      ├─main
│      └─user
└─tests
复制代码

其中blueprints就是蓝本文件夹,里面存放着和templates对应的4个蓝本

├─blueprints
│      auth.py
│      main.py
│      user.py
│      __init__.py
复制代码

这4个蓝本中__init__.py负责Python的包结构,其余三个则是app的3个功能模块:认证、主页、用户

以auth.py为例

from flask import Blueprint, ...
...(此处省略了一堆import)

bp = Blueprint('auth', __name__)

@bp.route('/login', methods=['GET', 'POST'])
def login():
    ...

@bp.route('/register', methods=['GET', 'POST'])
def register():
    ...

@bp.route('/logout')
def log_out():
    ...
复制代码

蓝本也可以注册路由,如果反向构造url的话就得加上蓝本名,比如url_for('auth.login')

蓝本的注册应在工厂函数中执行,并且每个蓝本可以通过url_prefix来给url添加前缀

常用插件

  • flask-admin:提供admin管理后台
  • flask-avatars:生成用户头像
  • flask-ckeditor:集成富文本编辑器ekeditor
  • flask-cors:提供跨域支持
  • flask-dropzone:集成文件上传插件dropzone
  • flask-login:处理用户登陆认证逻辑
  • flask-mail:邮箱服务
  • flask-migrate:提供数据库迁移支持
  • flask-moment:提供时间规范化支持
  • flask-mongoengine:集成mongoengine——面向 mongodb 的ORM
  • flask-restful:RESTful API支持
  • flask-socketio:集成socketio,常用于编写聊天室
  • flask-sqlalchemy:集成sqlalchemy——面向标准 SQL 的ORM
  • flask-weasyprint:提供PDF打印功能
  • flask-whooshee:集成whooshee——全文搜索引擎
  • flask-wtf:集成wtforms——表单支持
  • bootstrap-flask:集成bootstrap,并提供一些有用的宏
  • faker:能生成假数据,用于测试

高级玩法

强制响应格式

API返回的一般都是json,故在每个视图函数中调用jsonify将dict序列化为json

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def index():
    return jsonify({'message': 'Hello World!'})

@app.route('/foo')
def foo():
    return jsonify({'message': 'Hello foo!'})
复制代码

但其实没必要这么做,因为 flask的Response是可以定制的

flask的app实例提供了response_class属性,默认是Response

继续查照定义,发现Response其实继承了werkzeug里的BaseResponse

通过查阅BaseResponse,我们可以重载Response的force_type类方法,将类型为dict的response直接jsonify,并且无需在每个视图函数中都显式调用jsonify函数了

from flask import Flask, jsonify, Response

class JSONResponse(Response):
    @classmethod
    def force_type(cls, response, environ=None):
        if isinstance(response, dict):
            response = jsonify(response)
        return super().force_type(response, environ)

app = Flask(__name__)
app.response_class = JSONResponse

@app.route('/')
def index():
    return {'message': 'Hello World!'}

@app.route('/foo')
def foo():
    return {'message': 'Hello foo!'}
复制代码

当然,你也可以类似地强制响应格式为xml, RSSHub 就是这么实现的

from flask import Flask, Response

class XMLResponse(Response):
    def __init__(self, response, **kwargs):
        if 'mimetype' not in kwargs and 'contenttype' not in kwargs:
            if response.startswith('<?xml'):
                kwargs['mimetype'] = 'application/xml'
        return super().__init__(response, **kwargs)

app = Flask(__name__)
app.response_class = XMLResponse

@bp.route('/feed')
def rss_feed():
    from rsshub.spiders.feed import ctx
    return render_template('main/atom.xml', ctx())
复制代码

全局模板函数

有的时候你希望把某种逻辑抽象成一个函数,使得多个页面能共用,那么就要定义全局模板函数了

有的网站的 排序 功能是这么实现的:通过在url的查询字符串中追加sort(要排序的字段)和order(升序还是降序)参数,在视图函数中获取这2个参数并进行相应的排序处理

要实现这个功能,可以编写2个全局函数:1个负责修改url的查询字符串,另一个负责处理给查询排序

@bp.app_template_global()
def modify_querystring(**new_values):
    args = request.args.copy()
    for k, v in new_values.items():
        args[k] = v
    return f'{request.path}?{url_encode(args)}'

@bp.app_template_global()
def get_article_query():
    sort_key = request.args.get('s', 'date')
    order = request.args.get('o', 'desc')
    article_query = Article.query
    if sort_key:
        if order == 'asc':
            article_query = article_query.order_by(db.asc(sort_key))
        else:
            article_query = article_query.order_by(db.desc(sort_key))
    return article_query
复制代码

自定义路由转换器

我们都知道flask的路由映射是存储在app.url_map中的,因此查阅官方文档有关url_map的部分,就能轻松实现

from flask import Flask
from urllib.parse import unquote
from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):
    def __init__(self, url_map, separator='+'):
        super().__init__(url_map)
        self.separator = unquote(separator)

    def to_python(self, value): # 把路径转换成一个Python对象,比如['python', 'javascript', 'sql']
        return value.split(self.separator)

    def to_url(self, values): # 把参数转换成符合URL的形式,比如/python+javascript+sql/
        return self.separator.join(BaseConverter.to_url(self, value) for value in values)

app = Flask(__name__)
app.url_map.converters['list'] = ListConverter

@app.route('/list1/<list:var>/')
def list1(var):
    return f'Separator: + {var}'


@app.route('/list2/<list(separator=u"|"):var>/')
def list2(var):
    return f'Separator: | {var}'
复制代码

官方文档仅仅用了逗号分隔符,而在这里我们通过添加了separator属性来实现了自定义分隔符 访问如下链接体验下效果

http://localhost:5000/list1/python+javascript+sql
http://localhost:5000/list2/python|javascript|sql
复制代码

以上所述就是小编给大家介绍的《flask核心知识》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Foundations of PEAR

Foundations of PEAR

Good, Nathan A./ Kent, Allan / Springer-Verlag New York Inc / 2006-11 / $ 50.84

PEAR, the PHP Extension and Application Repository, is a bountiful resource for any PHP developer. Within its confines lie the tools that you need to do your job more quickly and efficiently. You need......一起来看看 《Foundations of PEAR》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具