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:
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.
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
Install mezzanine-meze. This library uses Sphinx to generate HTML from reST when saving blog entries.
pip install mezzanine-meze
Follow the instructions on https://github.com/abakan/mezzanine-meze to configure the settings.
Add meze to the INSTALLED_APPS in settings.py.
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
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'] """
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> ...
Install any missing libraries during runtime:
$ pip install sphinx $ pip install south
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.
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 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
I overwrote the sphinx_build method like this.
With this, I was able to convert from reStructuredText to HTML without any issues.
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.
Comments