Python + Django 上で CMS を構築する、 Mezzanine というシステムがあります。 ブログエントリは標準形式では WYSIWYG な HTML エディタで書きますが、 reStructured Text (reST, rst) で書きたかったため調べてみました。
pygments でコードシンタックスハイライティングもしてくれるようになるため、便利です。
環境としては
です。これを書いている 2015年5月現在、Django は 1.8 が使えるのですが、 mezzanine が Django 1.6 までしか対応していなためこのようなバージョンになっています。
後述しますが、これでは Sphinx と mezzanine-meze のバージョン関係に不具合があります。
mezzanine のインストール方法も書いておきます。mac 内に環境を作ります。
$ pyvenv-3.4 .virtualenvs/my-blog $ workon my-blog $ pip install mezzanine # Django ごとインストールされます
Mezzanine プロジェクトをつくるには
$ mezzanine-project my_blog
mezzanine-meze をインストールします。これは、ブログ記事の保存時、 Sphinx を使って reST からHTMLを生成するようになります。
pip install pip install mezzanine-meze
https://github.com/abakan/mezzanine-meze
ここにある通り、設定をしていきます。
settings.py の INSTALLED_APPS に、meze を追加
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
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'] """
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> ...
実行時、不足しているライブラリをインストールします。
$ pip install sphinx $ pip install south
なぜ 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 から行っても良いでしょう。
ここまでは、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 に変換できるようになりました。
ちなみに、reST ではなく markdown を扱えるライブラリとして、mezzanine-mdown というのがあります。
こちらの動作としては、テンプレートが HTML ファイルを生成する際のテンプレートフィルタとして、markdown -> HTML の変換をしているようです。
pip install mezzanine-mdown
ではインストールできなかった(見つからなかった)ため、
easy_install mezzanine-mdown
でインストールしました。…が、使ってません。
コメント