Mezzanine で、reStructured text を使う

(コメント)

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

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

現在未評価

コメント

最近のツイート

  • ytyng

    ytyng @ytyng

    俺もスタバアプリにログインできないよ
    1 ヶ月, 1 週間 前

  • 安藤拓郎 Takuro Ando

    安藤拓郎 Takuro Ando @takuroando

    ytyng

    これまでいろんなグッズを作ってきたけど、今回は「お米」と聞いて買うしかないなと。今夜の夕食はコシヒカリ!箸もセットだし^^ https://t.co/01ucQx9qtw #腰乃展 #マンガ展 https://t.co/4VL2vOe0Og
    1 ヶ月, 4 週間 前

  • ytyng

    ytyng @ytyng

    講談社さんとやった全部入り電子書籍セットがギネスブックに登録されたよー https://t.co/rbkd3IYub0
    1 ヶ月, 4 週間 前