Flask驱动的静态站点生成器(译)

Published on Nov 19, 2012

翻译自:Dead easy yet powerful static website generator with Flask

纯意译……以下是正文,和jekyll很像的感觉,哈?34行代码完成一个静态站点生成器。


我想将我的在线身份统合到一个单独的托管地方很久了,因此有了你现在浏览的这个网站。我也寻找一个静态网站架构有段时间了,尝试了许多但一个也不中意。这真令人沮丧。

然后遇到Armin Ronacher的这个tweet:

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/=%E5%B0%86%E5%91%88%E7%8E%B0%E6%B8%B2%E6%9F%93%E5%90%8E%E7%9A%84%E7%BA%AF%E6%96%87%E6%9C%AC%E3%80%82%E6%B3%A8%E6%84%8F%E9%80%9A%E8%BF%87page%E5%AF%B9%E8%B1%A1%E8%8E%B7%E5%BE%97html%E5%B1%9E%E6%80%A7markdown%E6%BA%90%E7%A0%81%E8%A2%AB%E8%BD%AC%E6%8D%A2%E6%88%90html。

又一轮:添加模板

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>

注意=urlfor()=这个模板函数,这是我们使用Flask和Jinjia2生成url的方式。

现在用=page.html=模板来填充页面内容的布局:

```html {% extends "base.html" %}

{% block content %}

{{ page.title }}

{% 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)

见鬼,我们刚刚做了什么?

  • 创建了一个应用的模板;一个通用布局(base.html)和一个页面模板(page.html)
  • 我们使用=rendertemplate=函数对页面用页面模板装饰。
  • 页面模板扩展基本模板来避免在每个页面都复制粘帖相同的内容。

又一轮:在主页呈现页面列表

现在我们的主页弱爆了。我们让它列出所有存在的页面。

创建一个=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代码就完成了……不错吧?

当然,现在的版本弱爆了,你能一点一点的为它添加各种特性了。