Mezzanine で、reStructured text を使う

Django
2015-05-10 07:19 (10 years ago)
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

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

まだ評価がありません
著者は、アプリケーション開発会社 Cyberneura を運営しています。
開発相談をお待ちしています。

アーカイブ