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变成这个函数。