layout: post
title: “Flask驱动的静态站点生成器(译)”
discription: “翻译”
category: python
tags: [flask, python]
{% include JB/setup %}
翻译自:Dead easy yet powerful static website generator with Flask
纯意译……以下是正文,和jekyll很像的感觉,哈?34行代码完成一个静态站点生成器。
我想将我的在线身份统合到一个单独的托管地方很久了,因此有了你现在浏览的这个网站。我也寻找一个静态网站架构有段时间了,尝试了许多但一个也不中意。这真令人沮丧。
然后遇到Armin Ronacher的这个tweet:
Frozen-Flask is really, really useful. Should have used that earlier.
— Armin Ronacher (@mitsuhiko) February 6, 2012
Armin是Flask这个Python微框架的作者,我喜欢flask的简洁。所以这个tweet一个机灵,我便开始探索Frozen-Flask的玩法。
Frozen-Flask将Flask应用_冻结_成静态文件,这样你能够高速而无痛地部署它们。再佐以Flask-FlatPages,你获得了完美的生成静态站点工具集,这个站点将有所以你使用框架能得到的特性。
在新文件夹中创建个新的virtualenv,使用pip安装必要的包:
$ mkdir sample_project && cd !$
$ mkvirtualenv --no-site-packages `pwd`/env
$ source env/bin/activate
$ pip install Flask Frozen-Flask Flask-FlatPages
写我们第一个版本的sitebuilder.py
:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello World!"
if __name__ == "__main__":
app.run(port=8000)
运行它;你应该看到类似:
$ python sitebuilder.py
* Running on http://127.0.0.1:8000/
* Restarting with reloader
用浏览器打开_http://:127.0.0.1:8000_看是否正常。
Flask-FlatPages为你的Flask应用提供一套页面。相对动态页面从关系数据库构建,静态页面是从纯文本文件构建。
在你的项目跟文件夹下创建一个pages/
目录,新建一个hello-wolrd.md
扔进去:
$ mkdir pages
$ vi pages/hello-world.md
hello-world.md
文件:
title: Hello World
date: 2012-03-04
**Hello World**, from a *page*!
如你所见你可以在页面内容中写入Markdown。所以让我们重新写我们的应用来为通过文件名为任何纯文本提供服务。
from flask import Flask
from flask_flatpages import FlatPages
DEBUG = True
FLATPAGES_AUTO_RELOAD = DEBUG
FLATPAGES_EXTENSION = '.md'
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
@app.route('/')
def index():
return "Hello World"
@app.route('/<path:path>/')
def page(path):
return pages.get_or_404(path).html
if __name__ == '__main__':
app.run(port=8000)
现在访问http://127.0.0.1:8000/hello-world/
将呈现渲染后的纯文本。注意通过page对象获得html属性markdown源码被转换成html。
flask使用jinja2模板引擎,让我们创建一些模板来装饰页面。首先在项目根目录下创建一个templates
文件夹:
$ mkdir templates
在templates/base.html
中创建基本布局:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>My site</title>
</head>
<body>
<h1><a href="{{ url_for("index") }}">My site</a></h1>
{% block content %}
<p>Default content to be displayed</p>
{% endblock content %}
</body>
</html>
注意url_for()
这个模板函数,这是我们使用Flask和Jinjia2生成url的方式。
现在用page.html
模板来填充页面内容的布局:
{% extends "base.html" %}
{% block content %}
<h2>{{ page.title }}</h2>
{{ page.html|safe }}
{% endblock content %}
我们的应用现在应该是:
from flask import Flask, render_template
from flask_flatpages import FlatPages
DEBUG = True
FLATPAGES_AUTO_RELOAD = DEBUG
FLATPAGES_EXTENSION = '.md'
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
@app.route('/')
def index():
return "Hello World"
@app.route('/<path:path>/')
def page(path):
page = pages.get_or_404(path)
return render_template('page.html', page=page)
if __name__ == '__main__':
app.run(port=8000)
见鬼,我们刚刚做了什么?
render_template
函数对页面用页面模板装饰。现在我们的主页弱爆了。我们让它列出所有存在的页面。
创建一个templates/index.html
:
{% extends "base.html" %}
{% block content %}
<h2>List of stuff</h2>
<ul>
{% for page in pages %}
<li>
<a href="{{ url_for("page", path=page.path) }}">{{ page.title }}</a>
</li>
{% else %}
<li>No stuff.</li>
{% endfor %}
</ul>
{% endblock content %}
随意地创建更多纯文本页面,就像我们创建hello-world.md
一样,将文件保存在pages/
目录下,使用.md
扩展名。
我们应用中的index()
路径现在应该这样:
重载主页,页面列表将呈现。真他×简单。
Flask-FlatPages允许像我们创建和hello-world.md
的标题和日期一样添加元数据,并且通过page.meta
来存取它们,获得看上去蠢蠢的python字典。真令人吃惊,不是吗?
让我们假设想要给页面添加标签,我们的hello-world.md
将变成:
title: Hello World
date: 2012-03-04
tags: [general, awesome, stuff]
**Hello World**, from a *page*!
元数据用YAML描述,因此你能够使用字符串、布尔、整数、浮点、列表、甚至字典,它们将转换成Python相应的内在等价物。
我们将使用两个包含共享部分的不同的模板来列出普通页面和标签页面,index.html
现在应该是:
{% extends "base.html" %}
{% block content %}
<h2>List of stuff</h2>
{% with pages=pages %}
{% include "_list.html" %}
{% endwith %}
{% endblock content %}
创建tag.html
模板,它将用来呈现标签页面列表:
{% extends "base.html" %}
{% block content %}
<h2>List of stuff tagged <em>{{ tag }}</em></h2>
{% with pages=pages %}
{% include "_list.html" %}
{% endwith %}
{% endblock content %}
新建的_list.html
模板应该包含:
<ul>
{% for page in pages %}
<li>
<a href="{{ url_for("page", path=page.path) }}">{{ page.title }}</a>
{% if page.meta.tags|length %}
| Tagged:
{% for page_tag in page.meta.tags %}
<a href="{{ url_for("tag", tag=page_tag) }}">{{ page_tag }}</a>
{% endfor %}
{% endif %}
</li>
{% else %}
<li>No page.</li>
{% endfor %}
</ul>
向应用中添加新的tag路径,使用新的tag.html
模板:
@app.route('/tag/<string:tag>/')
def tag(tag):
tagged = [p for p in pages if tag in p.meta.get('tags', [])]
return render_template('tag.html', pages=tagged, tag=tag)
注:若你之前不喜欢python的列表推导式,现在你会了。
好的,现在我们只要有一个动态网站,为存储在文件系统上的纯文本页面提供服务:废话。但是我们当然不是Flask应用而是一堆静态文件,省去了任何应用服务器。
来进入Frozen-Flask。它的使用真××简单:
import sys
from flask import Flask, render_template
from flask_flatpages import FlatPages
from flask_frozen import Freezer
DEBUG = True
FLATPAGES_AUTO_RELOAD = DEBUG
FLATPAGES_EXTENSION = '.md'
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
freezer = Freezer(app)
@app.route('/')
def index():
return render_template('index.html', pages=pages)
@app.route('/tag/<string:tag>/')
def tag(tag):
tagged = [p for p in pages if tag in p.meta.get('tags', [])]
return render_template('tag.html', pages=tagged, tag=tag)
@app.route('/<path:path>/')
def page(path):
page = pages.get_or_404(path)
return render_template('page.html', page=page)
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == "build":
freezer.freeze()
else:
app.run(port=8000)
然后运行:
$ python sitebuilder.py build
打开构建文件夹键入如下命令:
$ tree
.
├── hello-world
│ └── index.html
├── index.html
└── tag
├── awesome
│ └── index.html
├── general
│ └── index.html
└── stuff
└── index.html
5 directories, 5 files
你现在能部署build
目录下的任何文件到任何能托管静态文件的地方了,而你仅仅用34行Python代码就完成了……不错吧?
当然,现在的版本弱爆了,你能一点一点的为它添加各种特性了。