How to Render Markdown Content in a Django Application

Table of Contents

Introduction
Project Setup
Creating the Blog Model
Admin Interface
Rendering Markdown Safely
Views and Templates
Testing It Out
Conclusion


Introduction

Markdown is a lightweight markup language that's widely used for writing formatted content in a simple, readable way. If you're building a Django application—especially a blog, documentation platform, or CMS—you may want to allow users to write content in Markdown and render it as HTML on your pages.

In this post, you'll learn how to set up a Django project that accepts Markdown content and displays it safely in templates using the popular markdown Python package. We’ll also cover syntax highlighting using Pygments and how to protect your app from potential XSS vulnerabilities.


Project Setup

Install Django and Dependencies

First, create a virtual environment and install Django:

python -m venv venv
source venv/bin/activate
pip install django markdown pygments

Create your Django project and app:

django-admin startproject markdown_site
cd markdown_site
python manage.py startapp blog

Register the App

In markdown_site/settings.py, add 'blog' to the INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'blog',
]

Creating the Blog Model

In blog/models.py, define a simple blog post model:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField(help_text="Write your post using Markdown syntax.")
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

Then migrate your models:

python manage.py makemigrations
python manage.py migrate

Admin Interface

To easily create and manage posts:

In blog/admin.py:

from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'created_at')
    search_fields = ('title',)

Rendering Markdown Safely

Use the markdown package with extensions to render Markdown into HTML.

Create a helper in blog/utils.py:

import markdown
from django.utils.safestring import mark_safe

def render_markdown(text):
    md = markdown.Markdown(extensions=['fenced_code', 'codehilite'])
    html = md.convert(text)
    return mark_safe(html)

The mark_safe() function ensures Django doesn’t auto-escape the rendered HTML.


Views and Templates

Create a View

In blog/views.py:

from django.shortcuts import render, get_object_or_404
from .models import Post
from .utils import render_markdown

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    rendered_content = render_markdown(post.content)
    return render(request, 'blog/post_detail.html', {'post': post, 'content': rendered_content})

Create a URL Pattern

In blog/urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]

Then include this in the main urls.py of the project:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

Generate stylesheet using pygmentize command

pygmentize -S default -f html -a .codehilite > styles.css

Create a Template

In blog/templates/blog/post_detail.html:

<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'styles.css' %}">
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p><em>{{ post.created_at }}</em></p>
    <div>
        {{ content|safe }}
    </div>
</body>
</html>

Include Table of Contents

In order for table of content to work properly, the toc extension must be enabled explicitly

md = markdown.Markdown(extensions=['fenced_code', 'codehilite', 'toc'])

Testing It Out

Start the development server:

python manage.py runserver

Go to http://127.0.0.1:8000/admin, log in, and create a new blog post using Markdown. Visit /post/1/ to view the rendered HTML with syntax-highlighted code blocks.


Conclusion

Rendering Markdown in Django allows users to write and manage rich text content easily. With libraries like markdown and pygments, you can support features like fenced code blocks, syntax highlighting, and safe HTML output.

For enhanced security, especially when dealing with user-generated Markdown, consider adding additional sanitization with packages like bleach or use markdown extensions that strip unsafe tags.

This setup provides a solid foundation for building content-heavy applications, such as blogs, wikis, or documentation sites, with Markdown support in Django.