Mezzanine で、reStructured text を使う

投稿者: ytyng 8 年, 10 ヶ月 前

Python + Django 上で CMS を構築する、 Mezzanine というシステムがあります。 ブログエントリは標準形式では WYSIWYG な HTML エディタで書きますが、 reStructured Text (reST, rst) で書きたかったため調べてみました。

pygments でコードシンタックスハイライティングもしてくれるようになるため、便利です。

環境としては

  • Python 3.4
  • Django==1.6.11
  • Mezzanine==3.1.10
  • mezzanine-meze==0.3
  • Sphinx==1.3.1

です。これを書いている 2015年5月現在、Django は 1.8 が使えるのですが、 mezzanine が Django 1.6 までしか対応していなためこのようなバージョンになっています。

後述しますが、これでは Sphinx と mezzanine-meze のバージョン関係に不具合があります。

1. Mezzanine のインストール

mezzanine のインストール方法も書いておきます。mac 内に環境を作ります。

$ pyvenv-3.4 .virtualenvs/my-blog
$ workon my-blog
$ pip install mezzanine  # Django ごとインストールされます

Mezzanine プロジェクトをつくるには

$ mezzanine-project my_blog

2. reStructured Text 用ライブラリ mezzanine-meze のインストール

mezzanine-meze をインストールします。これは、ブログ記事の保存時、 Sphinx を使って reST からHTMLを生成するようになります。

pip install pip install mezzanine-meze

3. 設定

https://github.com/abakan/mezzanine-meze

ここにある通り、設定をしていきます。

3-1. INSTALLED_APPS の登録

settings.py の INSTALLED_APPS に、meze を追加

3-2. 追加フィールドの登録

settings.py の下部に追記

help_text = ("Source in reStructuredText format will be converted to "
             "HTML and result will replace content field.")
EXTRA_MODEL_FIELDS = (
    # Enable Meze for blog posts
    ("mezzanine.blog.models.BlogPost.source",
     "TextField", (), {"blank": True, "help_text": help_text}),
    ("mezzanine.blog.models.BlogPost.convert",
     "BooleanField", ("Convert source",), {"default": True}),
    # Enable Meze for rich text pages
    ("mezzanine.pages.models.RichTextPage.source",
     "TextField", (), {"blank": True, "help_text": help_text}),
    ("mezzanine.pages.models.RichTextPage.convert",
     "BooleanField", ("Convert source",), {"default": True}),
)
del help_text

3-3. MEZE_SETTINGS, SPHINX_CONF の登録

settings.py の下部に追記

MEZE_SETTINGS = {
    'workdir': os.path.join(PROJECT_ROOT, 'meze_workdir'),
}

SPHINX_CONF = """
project = u''
copyright = u''
version = '0'
release = '0'
master_doc = 'index'
pygments_style = 'sphinx'
html_theme = 'default'
html_sidebars = {'**': []}
html_domain_indices = False
html_use_index = False
html_show_sourcelink = False
html_add_permalinks = None
source_suffix = '.rst'
intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
extlinks = {'wiki': ('http://en.wikipedia.org/wiki/%s', ''),}
extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.extlinks']
"""

3-4. HTMLテンプレートの修正

HTMLテンプレート (例: base.html) に、Pygments の設定を追加します。

{% compress css %}
...
<link rel="stylesheet" href="{% static "meze/css/meze.css" %}">
<link rel="stylesheet" href="{% static "meze/css/pygments.css" %}">
...
{% compress js %}
...
<script src="{% static "meze/js/copybutton.js" %}"></script>
...

3-5. 追加ライブラリのインストール

実行時、不足しているライブラリをインストールします。

$ pip install sphinx
$ pip install south

3-6. DBマイグレーション

なぜ south をインストールしたかというと、3-2 でモデルにフィールドを追加しているため、 それを DB に反映しなければならないためです。

少しトリッキーな方法ですが、プロジェクト直下に、migrations ディレクトリを作って、 そこに south のマイグレーションファイルを入れ、マイグレーションさせます。

my_blog ディレクトリは、manage.py が含まれる Django のプロジェクトディレクトリですが、 それを django の app ディレクトリとしても認識させます。

settings.py の INSTALLED_APPS に my_blog を入れておき

$ cd my_blog
$ mkdir migrations
$ touch modles.py
$ ./manage.py schemamigration blog --auto --stdout >> migrations/0001_blog_customization.py
$ ./manage.py schemamigration pages --auto --stdout >> migrations/0002_pages_customization.py
$ ./manage.py migrate my_blog

こんな感じでマイグレーションできると思います。

データが無い場合は、DBを全部消して、./manage.py createdb から行っても良いでしょう。

4. 追加設定

ここまでは、mezzanine-meze のチュートリアルにある通りなのですが、 このまま起動すると meze/meze.py の 195行目

rst = os.path.join(workdir, 'index' + app.config.source_suffix)

ここで

TypeError at /admin/blog/blogpost/add/

Can't convert 'list' object to str implicitly

が出てしまいます。

おそらく、Sphix の現行バージョンでは、app.config.source_suffix が str ではなく list に 変わったのでしょう。

Sphinx の昔のバージョンをインストールすれば良いかもしれませんが、私はモンキーパッチを書いて対応しました。

urls.py の下部かどこかで

# Meze monkey patching

from meze.meze import os, sys, codecs, time, messages, Sphinx, SPHINX_CONF, SPHINX_CONF_APPEND
from meze.meze import Meze, MezeBuilder, MezeStream


def sphinx_build_monkey(self):
    workdir = self._workdir
    if not os.path.isdir(workdir):
        os.makedirs(workdir)

    conf = os.path.join(workdir, 'conf.py')
    with codecs.open(conf, encoding='utf-8', mode='w') as out:
        out.write(SPHINX_CONF)
        out.write(SPHINX_CONF_APPEND)

    start = time.time()
    status = MezeStream(sys.stdout)
    warning = MezeStream(sys.stderr)
    app = Sphinx(srcdir=workdir, confdir=workdir, outdir=workdir,
                 doctreedir=workdir, buildername='meze',
                 confoverrides={}, status=status, warning=warning,
                 freshenv=False, warningiserror=False, tags=[])

    if isinstance(app.config.source_suffix, (list, tuple)):
        rst = os.path.join(workdir, 'index' + app.config.source_suffix[0])
    else:
        rst = os.path.join(workdir, 'index' + app.config.source_suffix)

    with codecs.open(rst, encoding='utf-8', mode='w') as out:
        out.write(self._source)

    app.build(False, [rst])

    self._messages.append((messages.INFO, 'Source was converted '
                                          'into HTML using Sphinx in {:.2f}'.
                           format(time.time() - start)))
    for msg in warning.messages:
        items = msg.split('WARNING: ')
        if len(items) == 2:
            msg = items[1]
        self._messages.append((messages.WARNING, msg))

    self._content, MezeBuilder.context = MezeBuilder.context['body'], None


Meze.sphinx_build = sphinx_build_monkey

このように、sphinx_build メソッドを上書きしました。

これで、私は問題なく reStructuredText から HTML に変換できるようになりました。

5. markdown の場合

ちなみに、reST ではなく markdown を扱えるライブラリとして、mezzanine-mdown というのがあります。

こちらの動作としては、テンプレートが HTML ファイルを生成する際のテンプレートフィルタとして、markdown -> HTML の変換をしているようです。

pip install mezzanine-mdown

ではインストールできなかった(見つからなかった)ため、

easy_install mezzanine-mdown

でインストールしました。…が、使ってません。

現在未評価

コメント

アーカイブ

2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011