Using reStructuredText with Mezzanine

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.
Using pygments, it also provides code syntax highlighting, which is convenient.
The environment is as follows:
- Python 3.4
- Django==1.6.11
- Mezzanine==3.1.10
- mezzanine-meze==0.3
- Sphinx==1.3.1
As of May 2015, Django 1.8 is available, but Mezzanine only supports up to Django 1.6, so these versions are being used.
As mentioned later, this setup has issues with the version compatibility between Sphinx and mezzanine-meze.
1. Installing Mezzanine
Here is the method to install Mezzanine. The environment is set up within a Mac.
$ pyvenv-3.4 .virtualenvs/my-blog $ workon my-blog $ pip install mezzanine # Django is installed along with Mezzanine
To create a Mezzanine project:
$ mezzanine-project my_blog
2. Installing the reStructured Text Library mezzanine-meze
Install mezzanine-meze. This library uses Sphinx to generate HTML from reST when saving blog entries.
pip install mezzanine-meze
3. Configuration
Follow the instructions on https://github.com/abakan/mezzanine-meze to configure the settings.
3-1. Registering INSTALLED_APPS
Add meze to the INSTALLED_APPS in settings.py.
3-2. Registering Additional Fields
Add the following to the bottom of 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. Registering MEZE_SETTINGS and SPHINX_CONF
Add the following to the bottom of 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. Modifying HTML Templates
Add Pygments settings to your HTML template (e.g., base.html):
{% 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. Installing Additional Libraries
Install any missing libraries during runtime:
$ pip install sphinx $ pip install south
3-6. Database Migration
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.
A somewhat tricky method is to create a migrations directory directly under the project, put the South migration files there, and migrate them.
The my_blog directory contains the manage.py file and is recognized as a Django app directory.
Add my_blog to INSTALLED_APPS in settings.py.
$ cd my_blog $ mkdir migrations $ touch models.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
With this, you should be able to migrate.
If there is no data, you can delete the entire database and start from ./manage.py createdb.
4. Additional Configuration
Following the Mezzanine-meze tutorial up to this point, when you start it, you get an error at line 195 of meze/meze.py:
rst = os.path.join(workdir, 'index' + app.config.source_suffix)
Here, you get:
TypeError at /admin/blog/blogpost/add/Can't convert 'list' object to str implicitly
This is probably because in the current version of Sphinx, app.config.source_suffix has changed from str to list.
Instead of installing an older version of Sphinx, I wrote a monkey patch to handle this.
Add the following somewhere at the bottom of urls.py:
# Meze monkey patchingfrom 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'], NoneMeze.sphinx_build = sphinx_build_monkey
I overwrote the sphinx_build method like this.
With this, I was able to convert from reStructuredText to HTML without any issues.
5. Using Markdown
Incidentally, there is a library called mezzanine-mdown for handling markdown instead of reST.
It appears that this library converts markdown to HTML as a template filter when generating HTML files from templates.
pip install mezzanine-mdown
However, I couldn't install it with pip (it wasn't found), so I used:
easy_install mezzanine-mdown
But I haven't actually used it.
We look forward to discussing your development needs.