Frozen-Flask说明

Frozen-Flask(原名Flask-Static)是一个基于Flask的Static(静态)网页生成器。它可以轻松地冻结你现在已有的Flask项目,生成一系列的静态网页(例如:静态博客),然后直接搭载在HTTP服务器上而不需要安装其他的软件。相对于动态网页,Frozen-Flask生成的网站更易于托管,比如Github-Pages或Coding-Pages上面。同时当你的网站足够庞大以至于静态网页不能满足你的需求时,与其他静态网页生成器相比,基于Flask的Frozen-Flask可以让你的网站更快捷地迁移到动态的Flask上面。本文带你进一步了解这个神奇的工具。

(翻译自官方文档,英文版请点击这里

安装

通过以下其中一个安装本扩展

$ easy_install Frozen-Flask

如果你安装了pip组件

$ pip install Frozen-Flask

或者你可以从Github获得源代码

上下文

这篇文档假设你已经有一个工作中的Flask应用。你可以通过开发服务器运行测试

from myapplication import app
app.run(debug=True)

Frozen-Flask用于部署:取代了安装python, WSGI服务器和Flask,你可以使用Frozen-Flask 来冻结你的应用并且可以只部署静态HTML文件在你的服务器上。

开始吧

创建一个冻结器实例和你的app对象放在一起,然后调用它的freeze()方法。将其放入一个freeze.py的脚本文件(名字无所谓其实)

1
2
3
4
5
6
7
from flask_frozen import Freezer
from myapplication import app

freezer = Freezer(app)

if __name__ == '__main__':
    freezer.freeze()

这样会在你的static和templates目录旁边创建一个build文件夹,里面放着创建出来的静态文件。

Note

Frozen-Flask considers it “owns” its build directory. By default, it will silently overwrite files in that directory, and remove those it did not create.

The [configuration]() allows you to change the destination directory, or control what files are removed if at all.

This build will be most likely be partial since Frozen-Flask can only guess so much about your application.

寻找URL地址

Frozen-Flask 通过在WSGI层模拟请求并且写出返回信息给相应的文件。所以它需要知道在你的应用里面都有哪些URL链接。

下列URLs可以被自动找到 - 被你的Flask应用或者它的蓝图控制的静态文件 - 没有变量的URL地址,如果它们接受GET方法 - 0.6版新功能: 通过flask.url_for()请求的地址

这意味着如果你的应用在URL地址(没有参数)有索引页并且其他所有的相关页面可以通过那个地址递归调用url_for()找到,那么Frozen-Flask就可以全部自动地找到。

否则,你需要编写URL生成器

URL生成器

让我们假设你的应用看上去像这样

1
2
3
4
5
6
7
8
@app.route('/')
def products_list():
    return render_template('index.html', products=models.Product.all())

@app.route('/product_<int:product_id>/')
def product_details():
    product = models.Product.get_or_404(id=product_id)
    return render_template('product.html', product=product)

如果因为某些原因,一些生产出来的页面不会被链接(或者它们不是通过url_for()调用), Frozen-Flask 不会找到它们。

为了告诉Frozen-Flask这些网页,在调用freeze()之前写一个URL生成器并且将它放在Freezer实例里面

1
2
3
4
@freezer.register_generator
def product_details():
    for product in models.Product.all():
        yield {'product_id': product.id}

Frozen-Flask 会调用url_for(endpoint,**values),来找到URL地址,endpoint是生成器函数的名字,values是被生成器调用的每一个dict字典

你可以指定一个不同的endpoint通过一个(endpoint,values)元组而不仅仅是values,或者你可以通过传递url_for 并且单单产生URLs字符串

另外,生成器函数不需要使用Python generators来使用yield,它们可以是可以被调用的 并且返回可迭代的对象

下面的例子都是一样的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@freezer.register_generator
def product_details():  # endpoint defaults to the function name
    # `values` dicts
    yield {'product_id': '1'}
    yield {'product_id': '2'}

@freezer.register_generator
def product_url_generator():  # Some other function name
    # `(endpoint, values)` tuples
    yield 'product_details', {'product_id': '1'}
    yield 'product_details', {'product_id': '2'}

@freezer.register_generator
def product_url_generator():
    # URLs as strings
    yield '/product_1/'
    yield '/product_2/'

@freezer.register_generator
def product_url_generator():
    # Return a list. (Any iterable type will do.)
    return [
        '/product_1/',
        # Mixing forms works too.
        ('product_details', {'product_id': '2'}),
    ]

多次生成一个URL是Ok的,Frozen-Flask会只创建一次。给不同的函数起同样的名字并不是一个好的习惯,但是这里仍然可以使用,因为它们只被装饰器调用。你可能有一个模块for views,另一个模块for freezer 和 URL generator, 所以相同的名字不是问题。

测试URL生成器

Frozen Flask 背后的点子是你可以直接使用Flask来开发并测试你的应用。况且在部署在服务器之前,非常有必要来测试你的URL生成器,以免有内容丢失。

你可以在浏览器打开新生成的静态HTML文件,但是链接可能不能使用。FREEZER_RELATIVE_URLS设置可以修复这个问题,但是添加一个可见的index.html到链接。或者使用[run()]方法来使用生成的网页来大件一个HTTP服务器。这样一来你就可以在上传之前确认是否工作正常:

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

Freeze.run()在服务前冻结你的应用并且当重载器kicks in.但是reloader只会查看Python的文件,而不是模板或静态文件。因为这样,你可能想要只用Freeze.run()来测试URL生成器,其他情况照常使用app.run()

Flask-Scriptmay come in handy here.

配置

Frozen-Flask可以通过Flask的配置系统。下列是可用配置属性。

FREEZER_BASE_URL

你的App应该呗安装到的地址。这个会影响flask.url_for()的输出结果:absolute URLS(with_external = True) 或者如果你的应用不在你的域名根目录下。默认是'http://localhost/'.

FREEZER_RELATIVE_URLS

如果被设置为True,Frozen-Flask 会patch Jinja的环境,url_for()会返回相对路径的URLs.默认为False.除非你明确使用relative_url_for(),python的代码是不会被影响的。

FREEZER_DEFAULT_MIMETYPE

如果不能根据文件扩展名来决定MIME类型时MIME的默认类型。如果你在使用Apache Web Server. 这应该会与Apache配置里的默认DefaultType的值对应。默认是application/octet-stream.(0.7版更新)

FREEZER_IGNORE_MIMETYPE_WARNINGS

如果被设置为True,当服务器返回的MIME类型和文件名扩展衍生出的MIME类型不同,Frozen-Flask不会显示Warning.默认是False。(0.8版更新)

FREEZER_DESTINATION

到生成的静态网站的目录的路径。默认在build文件夹,static/templates文件夹旁边。

FREEZER_REMOVE_EXTRA_FILES

默认为True,Frozen-Flask将会把目标文件夹里面不是这次build的出来的文件删除。(就是删除原来的程序)。这是为了清除上次冻结器产生的文件,而这些文件已经不再需要了。将此项设置为False等同于设置FREEZER_DESTINATION_IGNORE为['*'].

FREEZER_DESTINATION_IGNORE

一个(默认为空的)列表的fnmatch patterns.目标路径内符合pattern的文件或文件夹不会被移除,即便是FREEZER_REMOVE_EXTRA_FILES 为True。就像 .gitignore文件一样,作用于整个路径如果它有一个/,而且作用于每一个被/分隔的部分,否则,比如,这会被设置成['.git*']如果默认是一个git仓库。

FREEZER_STATIC_IGNORE

一个(默认为空的)列表的fnmatch patterns,文件通过发送_static_文件that match any of the patterns are not coppied to the build directory. As in .gitignore files, patterns apply to the whole path if they contain a slash /, to each slash-separated part otherwise. For example, this could be set to ['*.scss'] to stop all SASS files from being frozen.

FREEZER_IGNORE_404_NOT_FOUND

如果设置为True(默认False),Frozen-Flask不会在遇到404错误返回时停止你的应用。在这种情况下,会有一个警告会被显示并且会使用你的404错误处理器或Flask默认设置来生成一个404错误网页。这项功能在你开发过程中网页链接到还没有写的网页时很有用。(0.12版更新)

文件名和MIME类型

对于每一个URL,Frozen-Flask模拟一个请求并且将内容保存在FREEZER_DESTINATION 下的文件夹内。文件名字是根据URL地址来定的。URL有一个尾斜杠来表示一个文件夹名,内容存放于该文件夹下index.html下.

被从URL里面移除的查询字符串用来建立文件名字。例如/lorem/?page=ipsum被保存为lorem/index.html. URL只有在它们的查询字符串相同时不同。它们应该返回相同的结果,否则,行为会被当做未定义行为。

另外,扩展检查了文件扩展名是否与Content-Type HTTP response header提供的MIME类型相匹配,Content-Type是一个静态web服务器可能会传送不是你期望的内容,这样Frozen-flask回发出警告

比如,下列的views 都是错的

1
2
3
4
5
6
7
@app.route('/lipsum')
def lipsum():
    return '<p>Lorem ipsum, ...</p>'

@app.route('/style.css')
def compressed_css():
    return '/* ... */'

作为结果Flask的Content-Type是text/html; charset=utf-8,但是Frozen-Flask和大多说web服务器通过文件名得到的MIME类型是application/octet-stream 和 text/css

上述代码可以这样通过添加URL的尾斜杠或者添加Content-Type

1
2
3
4
5
6
7
8
# Saved as `lipsum/index.html` matches the 'text/html' MIME type.
@app.route('/lipsum/')
def lipsum():
    return '<p>Lorem ipsum, ...</p>'

@app.route('/style.css')
def compressed_css():
    return '/* ... */', 200, {'Content-Type': 'text/css; charset=utf-8'}

你也可以通过配置来禁用。

字符编码

Flask内部使用Unicode,默认是UTF8来进行I/O操作.它会把一个MIME类型和编码方式发送到正确的Content-Type header。Frozen-Flask会尝试通过文件扩展名保存MIME类型,但是它不能保护编码元数据。你可能需要添加正确的标签到你的HTML。

对于URL,Flask的默认也是UTF8编码,所以你的web服务器会得到URL-encoded UTF-8 HTTP 请求。由你来转换这些到你的文件系统默认编码。Frozen-Flask总是使用Unicode描写文件名。

API

class flask_frozen.Freezer(app=None, with_static_files=True,with_no_argument_rules=True, log_url_for=True)

参数: - app(Flask实例):你的应用程序或者None如果你使用了init_app() - with_static_files (boolean)是否自动为静态文件生成Url地址 -with_no_argument_rules (boolean)是否对那些没有参数的log_url_for (boolean)URL规则去自动生成URL -log_url_for (boolean): 是否去log调用你的appmakes to url_for()并且通过它来生成URLs。

all_urls() 运行所有的生成器并生成相关与app根目录的URL地址对测试URL生成器非常有帮助

Note:
This does not generate any page, so URLs that are normally generated from url_for() calls will not be included here.

freeze() 清除目标目录并通过生成器建立所有地址

init_app(app) 允许在Freezer初始化后注册一个app 参数: app: 你的Flask应用

register_generator(function) 注册一个函数为URL生成器 这个函数应该返回一个可迭代的URL路径或者(endpoint, values) 元组 to be used as url_for(endpoint, **values).

root Frozen-Flask写入文件的绝对路径:resolved value for the FREEZER_DESTINATION configuration.

run(**options) 和serve()一样但是需要先冻结(调用freeze())

flask_frozen.walk_directory(root, ignore=()) 递归逐层进入根目录并且生成用'/'分割的相对路径。 之前被用来执行静态文件的URL生成器 参数:ignore: 一列fnmatch样式,用法类似.gitignore, patterns that contains a slash are matched against the whole path, others against individual slash-separated parts.

flask_frozen.relative_url_for(endpoint, **values) 类似于url_for(),但是在可能的情况下返回相关的URL

绝对URL(with _external=True or to a different subdomain)是不变的,但是/foo/bar 变成../bar,依赖于
当前请求的上下文路径(This, of course, requires a Flask request context.)

URLs 否则也会以 '/'结束来加载index.html。 也正因为如此,这个函数只可以和Frozen-Flask一起用,不可以在app.run()(Flask)或其他的WSGI服务器上使用。

如果FREEZER_RELATIVE_URLS配置是True,frozen-Flask会自动修补应用程序的Jinja环境来让模板中的url_for变成这个函数。