Render forum posts as Markdown
Python · Django · intermediate · modification
Forum posts finally get formatting. A `render_markdown` filter turns the body into HTML with Python-Markdown, using the fenced code, tables and sane lists extensions so code samples and tables from other sites paste in cleanly. The post page swaps `linebreaks` for the new filter; replies keep `linebreaks` until the next PR. Tried it on a long post with headings, a code block and a table, and on an empty body.
Django 5.1 community forum, `USE_TZ = True`, sessions with `SESSION_COOKIE_HTTPONLY = True`, CSRF middleware on. No Content-Security-Policy header is set. The `forum` app already has `templatetags/forum_tags.py` (with `reply_label`) and is in `INSTALLED_APPS`; `Post.body` is a non-null `TextField` of up to 20,000 characters written by the post's author. Staff moderate posts from the same post page while signed in to the admin. `markdown` 3.7 and `nh3` 0.2 are already in `requirements.txt`.
Requirements
- Posts are written in Markdown. The post page renders the body as HTML: headings, bold and italic, links, lists, block quotes, inline code, fenced code blocks, tables, horizontal rules and hard line breaks. Images are not supported in posts (they go in attachments). Replies stay plain text for now; they move to Markdown in a later PR.
- Anyone with an account can post, and every visitor reads posts, moderators and staff included. Nothing in a post body may become active content on the page: HTML typed into a post may produce at most the formatting listed in requirement 1 (scripts, styles, event-handler attributes, frames, forms and any other markup are dropped), and links may only use `http`, `https` or `mailto` (relative links to the forum are fine).
- Use the project's existing libraries: Python-Markdown 3.7 for rendering and `nh3` 0.2 for sanitising, as the profile "About me" field already does.
Files touched
- forum/templatetags/forum_tags.py
- forum/templates/forum/post_detail.html
--- forum/templatetags/forum_tags.py
+import markdown
from django import template
+from django.utils.safestring import mark_safe
register = template.Library()
+
+MARKDOWN_EXTENSIONS = ["fenced_code", "tables", "sane_lists"]
return "1 reply" if count == 1 else f"{count} replies"
+
[email protected]
+def render_markdown(text):
+ """Render a post body written in Markdown to HTML."""
+ html = markdown.markdown(text, extensions=MARKDOWN_EXTENSIONS)
+ return mark_safe(html)
+
--- forum/templates/forum/post_detail.html
<p class="post-meta">by {{ post.author.username }} · {{ post.reply_count|reply_label }}</p>
</header>
- <div class="post-body">{{ post.body|linebreaks }}</div>
+ <div class="post-body markdown">{{ post.body|render_markdown }}</div>
</article>
{% endblock %}