Hi, I'm Harlin and welcome to my blog. I write about Python, Alfresco and other cheesy comestibles.

Python: How to Set up Markdownx for Django

I am working on a blog app written with Django.

I've decided I don't want to deal with TinyMCE as the editor any longer and prefer to write it using Markdown.

I've found what appears to be a decent Markdown module for Django projects called MarkdownX. I really only need it for the content field for the Post model used in Django's admin console.

The documentation was not so linear to me and I had to play around with it a bit to get it to work but I decided to write up a quick and dirty setup so that anyone can get this to work in the same fashion.

First, let's get the module installed:

pip install django-markdownx

Next, add it to your settings.py file:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'markdownx', # <---
    'common',
    'posts',
]

Now, let's add it to our project's urls.py file:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^markdownx/', include('markdownx.urls')), # <----
    url(r'^post/', include('posts.urls')),
]

For my posts app, I did add this urls.py:

from django.conf.urls import url, include
from django.contrib import admin
from .views import post_detail


urlpatterns = [
    url(
        regex='^(?P<post_id>\d+)/$',
        view=post_detail,
        name='post_detail',
    ),
]

Then, let's add these lines to our models.py file:

from django.contrib.auth.models import User
from django.db import models
from markdownx.models import MarkdownxField # <---
from markdownx.utils import markdownify # <---


class Post(models.Model):
    title = models.CharField(max_length=100, unique=True)
    author = models.ForeignKey(User)
    content = MarkdownxField() # <--- This is our field we'll use.
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    is_published = models.BooleanField(default=False)

    @property 
    def formatted_markdown(self):  # <--- We'll need this for views.py later
        return markdownify(self.content)

    def __unicode__(self):
        return self.title

So that we can get this to show up in our admin console, let's add this in admin.py:

from django.contrib import admin
from django.db import models
from markdownx.admin import MarkdownxModelAdmin # <---
from .models import Post


admin.site.register(Post, MarkdownxModelAdmin) # <---

Finally, let's add a view and a template:

from django.shortcuts import render
from .models import Post

def post_detail(request, post_id):

    post = Post.objects.get(pk=post_id)

    return render(
        request,
        'posts_post.html',
        {
            'post': post,
        }
    )

and then posts_post.html:

<!DOCTYPE html>
<html>
<head>
    <title>Post</title>
</head>
<body>
    <h1>
        {{ post.title }}
    </h1>
    <p>Created: {{ post.created }}</p>

    <p>
        {{ post.formatted_markdown|safe }}
    </p>
</body>
</html>

Now, we should run these:

# ./manage.py makemigrations
# ./manage.py migrate

and then start up Django:

# ./manage.py runserver

Go to http://localhost:8000/admin and add a post. I added something like this:

# A Sample Blog Post

## Point #1

Here is some text. Did you ever notice that ... 

## Point #2

And here is a list to consider:

1. Item #1
2. Item #2
3. Item #3

## Point #3

And here is an unordered list to consider:

- Item 1
- Item 2
- Item 3

Anyways I do hope this was helpful. 

If you need to know more, you can always [Google](http://www.google.com).

As you use the MarkdownField() you will see the page rendered from markdown right below it. If you're not sure how to use Markdown I suggest this page.

I use Postgresql and the id for my new post is 1. So now, go to http://localhost:8000/post/1/ and you should see your post rendered as expected from Markdown.

Any Comments, Always Welcome!