使用Flask-Cache

作为一个微框架Flask没有内置的缓存功能,但是,有werkzeug cache API和一个很好的扩展,以提供其缓存功能到您的Flask应用程序,该扩展由@thadeusb创建,是非常容易实现和使用。

安装
在你的env通过PyPI安装它(推荐)

pip install Flask-Cache

如果您需要最近的更改或错误修复,您还可以直接从源代码安装它

pip install https://github.com/thadeusb/flask-cache/tarball/master

配置
您可以在应用设置中设置一组配置键,但最重要的是由CACHE_TYPE键定义的缓存后端。

缓存类型解析为一个导入字符串,它需要是一个对象实现werkzeug cache api,但是werkzeug.contrib.cache实现有一些别名

默认情况下,CACHE_TYPE是Null,这意味着您的应用程序将没有缓存,因此您需要选择以下选项:

null | No Cache - NullCache
simple | Will use in memory pickle and is recommended only for single process development server
memcached | Requires pylibmc or memcached and requires memcached configuration in settings
redis | Requires redis, werkzeug 0.7 and redis configuration in settings
filesystem | The same as simple but stores the pickle in a cache_dir
gaememcached | For Google AppEngine
saslmemcached | The same as memcached but for SASL - pylibmc required

完整的选项和配置变量在pythonhosted.org/Flask-Cache/#configuring-flask-cache

一个简单的Flask应用程序
一个名为 app.py的文件

import time
from flask import Flask

app = Flask(__name__)

@app.route("/")
def view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

运行python app.py,并在浏览器中打开https://localhost:5000,然后按F5(刷新)查看当前日期和时间。

在视图中启用Flask-Cache
现在我们要在这个小应用程序上启用缓存,以避免刷新当前时间(对于这个例子,我们使用当前时间作为返回,但想象它可能是一个大数据集或巨大的计算)

一个名为 cached_app.py的文件

import time
from flask import Flask

# import the flask extension
from flask.ext.cache import Cache   

app = Flask(__name__)

# define the cache config keys, remember that it can be done in a settings file
app.config['CACHE_TYPE'] = 'simple'

# register the cache instance and binds it on to your app 
app.cache = Cache(app)   

@app.route("/")
@app.cache.cached(timeout=300)  # cache this view for 5 minutes
def cached_view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

运行上面的python cached_app.py,并在浏览器中打开https://localhost:5000,然后按F5(刷新),看到当前日期和时间,现在缓存5分钟。

@ cache.cached decorator接受该视图的request.path并使用它作为缓存键,如果由于任何原因你需要一个不同的键,你可以传递一个key_prefix参数到装饰器。在这种情况下,如果您传递一个包含%s占位符的key_prefix,它将被当前的request.path所替换

以上是Flask应用程序和使用缓存的最简单和常规的例子,但如果你的应用程序是使用工厂模式,蓝图,基于类的视图或视图位于不同的模块,你将需要使用高级方法。

缓存常规函数
相同的缓存装饰器可以用于缓存常规函数,但在这种情况下,您需要指定key_prefix参数,否则它将使用request.path,如果您有许多缓存函数,它可能会导致冲突。

对于这个例子,我们将使用this模块并从Python之禅中随机提取引用一段。

名为cached_function_app.py的文件

import time
import random

from this import s, d
from string import translate, maketrans

from flask.ext.cache import Cache
from flask import Flask

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

@app.cache.cached(timeout=10, key_prefix="current_time")
def get_current_time():
    return time.ctime()

def random_zen_quote():
    """Pick a random quote from the Zen of Python""" 
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>It is cached:</strong> {cached}</li>
        <li><strong>It is not cached:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

现在运行python cached_function_app.py,并打开https://localhost:5000,当点击F5刷新,你会看到当前时间缓存5分钟和随机更新引用,你可以改变缓存来看效果。

def get_current_time():
    return time.ctime()

@app.cache.cached(timeout=10, key_prefix="zen_quote")
def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>It is not cached:</strong> {cached}</li>
        <li><strong>It is cached:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

注意:因为我们正在导入该this示例的模块,所以您将在Flask终端中看到禅的引用,对于这没有影响。

缓存modular views
现在这个例子,当你有你的应用程序分裂成两个或更多的文件,以更好的组织

在一个名为app文件夹中放3个文件__init__.py,app.py和views.py

app/__init__.py 是一个空文件

app/views.py

import time
import random
from this import s, d
from string import translate, maketrans

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

def zen_view():
    return """
    <h1>Cached for 10 seconds!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

现在你可以看到上面已经定义的视图函数文件,作为一个分离出来的文件,为避免循环导入,我们不建议使用@ app.route或者@ app.cache,所以这个视图将是应用程序无关,我们要注册其url规则和缓存在主应用程序文件中。

当你的应用有太多的视图,想要一个更好的组织,这种结构是需要的。

注意:为了更好的组织,主要推荐的模式是蓝图,我将进一步解释。

app/app.py

现在在主应用程序中,我们需要导入我们的视图,明确缓存装饰器,并注册其url。

from flask import Flask
from flask.ext.cache import Cache
from views import zen_view

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

# explicitly apply the cache in the old-style decoration way
cached_zen_view = app.cache.cached(timeout=10)(zen_view)

# explicitly register the cached view url mapping
app.add_url_rule("/", view_func=cached_zen_view)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

注意:您还可以在不同的文件中分离缓存实例,以便延迟加载,我们将在下一个示例中看到

缓存 Blueprint views
如前所述,在Flask应用程序中最好的模式是Blueprint模式,这是一种创建分离的“元应用程序”的方式,将在初始化时连接到主应用程序,这里的问题是蓝图是指以便可以由许多不同的应用程序重用,因此缓存控制调用应该被动态化。

为了避免循环导入,您需要创建与应用程序实例分离的缓存实例(如果您要构建更复杂的应用程序,可以考虑切换到应用程序工厂模式)。
按照以下结构创建一个blueprint_app

cached_blueprint_app/
├── app.py
├── cache.py
├── blueprints
│   ├── __init__.py
│   └── zen_blueprint.py
└── __init__.py

cache.py

from flask.ext.cache import Cache    
cache = Cache()

我们可以创建一个虚拟的延迟缓存实例,它将在以后当视图被调用时初始化。对于在应用程序中,我们将重新导入相同的缓存实例和调用init_app方法。

基础的 blueprints/zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from cache import cache

zen = Blueprint('zen', __name__)

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@zen.route("/")
@cache.cached(timeout=20)
def zen_view():
    return """
    <h1>Cached for 20 seconds!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

注意:在一个真正的应用程序中,你将需要模块化分离出来的视图,辅助等,并将你的蓝图打包到一个Python包。

主程序 app.py

from flask import Flask

from blueprints.zen_blueprint import zen
from cache import cache

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
cache.init_app(app)

app.register_blueprint(zen)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

注意,我们创建了一个缓存的虚拟实例cache.py然后使用该实例来装饰蓝图视图,然后使用init_app方法在app.py中初始化缓存。这是可能的,因为Flask初始化循环和Flask-Cache扩展中的优秀实现,处理这种情况,如果你打算写自己的Flask扩展看看Flask-Cache源代码。

通过调用运行应用程序python cached_blueprint_app/app.py并打开https://localhost:5000以查看缓存20秒的蓝图视图。

缓存MethodView

让我们使用相同的cached_blueprint_app例子,但将zen_viewin转换为一个MethodView

更改zen_blueprint.py为:

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from flask.views import MethodView
from cache import cache

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    @cache.cached(30)
    def get(self):
        return """
        <h1>Cached for 30 seconds!</h1>
        <ul>
            <li>{time}</li>
            <li>{quote}</li>
        </ul>
        """.format(
            time=self.get_current_time(),
            quote=self.random_zen_quote()
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])


zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

方法视图将HTTP方法名称作为GET,POST,DELETE映射到视图方式作为get,post,delete等,所以我们需要做的是创建一个方法get并使用装饰器装饰它@cache.cached。

注意:由于从调用者的角度来看隐式自我,你不能对视图的各个方法使用常规视图装饰器,但Flask-Cache是​​一个例外,因为它的实现允许在单个方法中使用缓存装饰器。记住这一点。
另外,你可能想要缓存所有的方法在一个视图,为了你可以缓存dispatch_request方法,或更好地,你可以装饰整个视图。

缓存调度程序

class ZenView(MethodView):
    @cache.cached(timeout=30)
    def dispatch_request(self):
        return super(ZenView, self).dispatch_request()

    ...

缓存整个视图(推荐)

zen = Blueprint('zen', __name__)

class ZenView(MethodView):
    ...

cached_zen_view = cache.cached(timeout=50)(ZenView.as_view('zen'))
zen.add_url_rule("/", view_func=cached_zen_view)

缓存template blocks
Flask缓存有一个能够缓存模板块的模板标签,让我们改变ZenView使用Jinja2模板

在 zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint, render_template
from flask.views import MethodView

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    def get(self):
        return render_template(
            'zen.html',
            get_random_quote=self.random_zen_quote
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])

zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

现在我们需要在中创建一个模板文件 cached_blueprint_app/templates/zen.html

<h3> Random Zen of Python </h3>
<strong>{{get_random_quote()}}</strong>

运行应用程序python cached_blueprint_app/app.py并打开http:// localhost:5000,你会看到一个随机报价刷新每次你推F5,让它缓存30秒。

更改zen.html模板

{% cache 30 %}
<h3> Random Zen of Python </h3>
<strong>{{get_random_quote()}}</strong>
{% endcache %}
现在保存文件并刷新以查看缓存30秒的内容。

使用memoize装饰器缓存函数和带有变量参数的视图

有时,yout视图和函数接收可能来自url映射或直接到函数调用的参数,您可能想缓存视图或功能,并使用参数作为缓存其不同结果的键,Flask-Cache有一个不同的装饰器那。

注意:对于没有接收参数的函数,cached()和memoize()实际上是相同的。
现在用一个简单的应用程序 memoize_app.py

现在运行python memoize_app.py并打开https://localhost:5000/yourname,并注意该函数将被缓存为您在url中作为参数传递的每个不同的名称。

缓存任意对象
有时候装饰器不能被使用,你需要在缓存上显式地设置或获取一些东西。

在视图或蓝图内,您可以使用current_app

from flask import current_app

def some_function():
    cached = current_app.cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    current_app.cache.set('a_key', result, timeout=300)
    return result

或者如果使用separete缓存实例,你可以直接这样做

from cache import cache

def function():
    cached = cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    cache.set('a_key', result, timeout=300)
    return result

清除缓存

您可以创建一个脚本来清除缓存,或者在需要时使用它的函数

from flask.ext.cache import Cache    
from yourapp import app
cache = Cache()

def main():
    cache.init_app(app)

    with app.app_context():
        cache.clear()

if __name__ == '__main__':
    main()

警告:某些后端实施不支持完全清除大小写。此外,如果你不使用密钥前缀,一些实现(例如Redis)将刷新整个数据库。确保您不在缓存数据库中存储任何其他数据。
有很多例子和很好记录的API在flask-Cache网站https://pythonhosted.org/Flask-Cache/,你也可以按照Flask-Cache 文档中的示例创建自己的缓存后端。

这是一篇翻译的文章,工具是谷歌翻译,看不懂也没办法!
文章出处:https://brunorocha.org/python/flask/using-flask-cache.html

2 thoughts on “使用Flask-Cache”

Leave a Comment