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.