---
slug: "Mezzanineで、reStructuredtextを使う"
title: "Using reStructuredText with Mezzanine"
description: "\n\nThere is a system called Mezzanine for building a CMS on Python + Django. By default, blog entries are written using a WYSIWYG HTML editor, but I wanted to write them using reStructured Text (reST, rst), so I did some research."
url: "https://www.ytyng.com/en/blog/Mezzanineで、reStructuredtextを使う"
publish_date: "2015-05-10T07:19:19Z"
created: "2015-05-10T07:19:19Z"
updated: "2026-02-27T10:53:56.708Z"
categories: ["Django"]
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20230812/ad19f15637a74a79b22f26796801c0cb.png.webp?width=768"
has_video: false
has_music: false
video_urls: []
music_urls: []
lang: "en"
---

# Using reStructuredText with Mezzanine

<div class="document">

<p>There is a system called Mezzanine for building a CMS on Python + Django. By default, blog entries are written using a WYSIWYG HTML editor, but I wanted to write them using reStructured Text (reST, rst), so I did some research.</p>
<p>Using pygments, it also provides code syntax highlighting, which is convenient.</p>
<p>The environment is as follows:</p>
<ul class="simple">
<li>Python 3.4</li>
<li>Django==1.6.11</li>
<li>Mezzanine==3.1.10</li>
<li>mezzanine-meze==0.3</li>
<li>Sphinx==1.3.1</li>
</ul>
<p>As of May 2015, Django 1.8 is available, but Mezzanine only supports up to Django 1.6, so these versions are being used.</p>
<p>As mentioned later, this setup has issues with the version compatibility between Sphinx and mezzanine-meze.</p>
<div class="section" id="mezzanine">
<h3>1. Installing Mezzanine</h3>
<p>Here is the method to install Mezzanine. The environment is set up within a Mac.</p>
<pre class="literal-block">$ pyvenv-3.4 .virtualenvs/my-blog
$ workon my-blog
$ pip install mezzanine  # Django is installed along with Mezzanine
</pre>
<p>To create a Mezzanine project:</p>
<pre class="literal-block">$ mezzanine-project my_blog
</pre>
</div>
<div class="section" id="restructured-text-mezzanine-meze">
<h3>2. Installing the reStructured Text Library mezzanine-meze</h3>
<p>Install mezzanine-meze. This library uses Sphinx to generate HTML from reST when saving blog entries.</p>
<pre class="literal-block">pip install mezzanine-meze
</pre>
</div>
<div class="section" id="id1">
<h3>3. Configuration</h3>
<p>Follow the instructions on <a class="reference external" href="https://github.com/abakan/mezzanine-meze">https://github.com/abakan/mezzanine-meze</a> to configure the settings.</p>
<div class="section" id="installed-apps">
<h4>3-1. Registering INSTALLED_APPS</h4>
<p>Add meze to the INSTALLED_APPS in settings.py.</p>
</div>
<div class="section" id="id2">
<h4>3-2. Registering Additional Fields</h4>
<p>Add the following to the bottom of settings.py:</p>
<pre class="literal-block">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
</pre>
</div>
<div class="section" id="meze-settings-sphinx-conf">
<h4>3-3. Registering MEZE_SETTINGS and SPHINX_CONF</h4>
<p>Add the following to the bottom of settings.py:</p>
<pre class="literal-block">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']
"""
</pre>
</div>
<div class="section" id="html">
<h4>3-4. Modifying HTML Templates</h4>
<p>Add Pygments settings to your HTML template (e.g., base.html):</p>
<pre class="literal-block">{% compress css %}
...
&lt;link rel="stylesheet" href="{% static "meze/css/meze.css" %}"&gt;
&lt;link rel="stylesheet" href="{% static "meze/css/pygments.css" %}"&gt;
...
</pre>
<pre class="literal-block">{% compress js %}
...
&lt;script src="{% static "meze/js/copybutton.js" %}"&gt;&lt;/script&gt;
...
</pre>
</div>
<div class="section" id="id3">
<h4>3-5. Installing Additional Libraries</h4>
<p>Install any missing libraries during runtime:</p>
<pre class="literal-block">$ pip install sphinx
$ pip install south
</pre>
</div>
<div class="section" id="db">
<h4>3-6. Database Migration</h4>
<p>South is installed because additional fields have been added to the model in step 3-2, and these need to be reflected in the database.</p>
<p>A somewhat tricky method is to create a migrations directory directly under the project, put the South migration files there, and migrate them.</p>
<p>The my_blog directory contains the manage.py file and is recognized as a Django app directory.</p>
<p>Add my_blog to INSTALLED_APPS in settings.py.</p>
<pre class="literal-block">$ cd my_blog
$ mkdir migrations
$ touch models.py
$ ./manage.py schemamigration blog --auto --stdout &gt;&gt; migrations/0001_blog_customization.py
$ ./manage.py schemamigration pages --auto --stdout &gt;&gt; migrations/0002_pages_customization.py
$ ./manage.py migrate my_blog
</pre>
<p>With this, you should be able to migrate.</p>
<p>If there is no data, you can delete the entire database and start from ./manage.py createdb.</p>
</div>
</div>
<div class="section" id="id4">
<h3>4. Additional Configuration</h3>
<p>Following the Mezzanine-meze tutorial up to this point, when you start it, you get an error at line 195 of meze/meze.py:</p>
<pre class="literal-block">rst = os.path.join(workdir, 'index' + app.config.source_suffix)
</pre>
<p>Here, you get:</p>
<pre class="literal-block">TypeError at /admin/blog/blogpost/add/

Can't convert 'list' object to str implicitly
</pre>
<p>This is probably because in the current version of Sphinx, app.config.source_suffix has changed from str to list.</p>
<p>Instead of installing an older version of Sphinx, I wrote a monkey patch to handle this.</p>
<p>Add the following somewhere at the bottom of urls.py:</p>
<pre class="literal-block"># 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
</pre>
<p>I overwrote the sphinx_build method like this.</p>
<p>With this, I was able to convert from reStructuredText to HTML without any issues.</p>
</div>
<div class="section" id="markdown">
<h3>5. Using Markdown</h3>
<p>Incidentally, there is a library called mezzanine-mdown for handling markdown instead of reST.</p>
<p>It appears that this library converts markdown to HTML as a template filter when generating HTML files from templates.</p>
<pre class="literal-block">pip install mezzanine-mdown
</pre>
<p>However, I couldn't install it with pip (it wasn't found), so I used:</p>
<pre class="literal-block">easy_install mezzanine-mdown
</pre>
<p>But I haven't actually used it.</p>
</div>
</div>
