Skip to main content

A GitHub-style markdown editor widget for Django forms

Project description

django-markdown-widget

A GitHub-style markdown editor widget for Django forms and admin.

PyPI version Python versions Django versions License: MIT Docs

Documentation | PyPI | GitHub

Features

  • GitHub-flavored markdown editor with live preview
  • Toolbar with common formatting actions (headings, bold, italic, code, tables, etc.)
  • Image and file uploads with drag & drop support
  • Light, dark, and auto theme support
  • Django admin integration via one-line mixin
  • Template tag and filter for rendering markdown in templates
  • Pluggable renderer and upload handler architecture
  • Automatic media cleanup for orphaned uploads
  • Management command for bulk orphan detection
  • Zero hard dependencies beyond Django (markdown library is optional)
  • Keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+K, etc.)

Installation

pip install django-markdown-widget

Add to your INSTALLED_APPS and include the URLs:

# settings.py
INSTALLED_APPS = [
    ...
    "django_markdown_widget",
]

# urls.py
from django.urls import include, path

urlpatterns = [
    ...
    path("md-editor/", include("django_markdown_widget.urls")),
]

For server-side rendering, install a markdown library:

pip install markdown

Quick Start

Form Widget

from django.forms import ModelForm
from django_markdown_widget import MarkdownEditorWidget

class PostForm(ModelForm):
    class Meta:
        model = Post
        fields = ["title", "content"]
        widgets = {
            "content": MarkdownEditorWidget(),
        }

Django Admin

from django.contrib import admin
from django_markdown_widget import MarkdownEditorAdminMixin

@admin.register(Post)
class PostAdmin(MarkdownEditorAdminMixin, admin.ModelAdmin):
    list_display = ["title", "created_at"]
    markdown_fields = ["content"]  # omit to apply to all TextFields

Template Rendering

{% load markdown_widget %}

{# As a filter (recommended) #}
{{ post.content|markdown }}

{# As a tag #}
{% markdown post.content %}

Media Cleanup

from django_markdown_widget import MarkdownCleanupMixin

class Post(MarkdownCleanupMixin, models.Model):
    content = models.TextField()
    # markdown_cleanup_fields = ["content"]  # optional: limit to specific fields

Enable in settings:

MD_EDITOR = {
    "CLEANUP_MEDIA": True,
}

Bulk cleanup via management command:

python manage.py cleanup_markdown_media --dry-run
python manage.py cleanup_markdown_media

Configuration

All settings are optional and go under MD_EDITOR in your Django settings:

MD_EDITOR = {
    # Pluggable backend classes
    "RENDERER_CLASS": "django_markdown_widget.renderers.DefaultRenderer",
    "UPLOAD_HANDLER_CLASS": "django_markdown_widget.uploads.DefaultUploadHandler",

    # Toolbar buttons
    "TOOLBAR": [
        "heading", "bold", "italic", "strikethrough", "separator",
        "quote", "code", "code-block", "link", "image", "separator",
        "ordered-list", "unordered-list", "task-list", "separator",
        "horizontal-rule", "table", "details", "separator",
        "highlight", "superscript", "subscript", "separator",
        "attach", "mention", "ref", "separator",
        "undo", "redo", "fullscreen",
    ],

    # Upload settings
    "ALLOWED_UPLOAD_TYPES": ["image/png", "image/jpeg", "image/gif", "image/webp"],
    "MAX_UPLOAD_SIZE": 10 * 1024 * 1024,  # 10 MB
    "UPLOAD_PATH": "md-editor/uploads/%Y/%m/",

    # Editor defaults
    "DEFAULT_HEIGHT": "300px",
    "PLACEHOLDER": "Add your comment here...",
    "THEME": "auto",  # "light", "dark", or "auto"

    # Security
    "REQUIRE_AUTH": True,

    # Media cleanup
    "CLEANUP_MEDIA": False,
}

Custom Renderer

from django_markdown_widget import BaseRenderer

class MyRenderer(BaseRenderer):
    def render(self, markdown_text: str) -> str:
        import markdown_it
        md = markdown_it.MarkdownIt()
        return md.render(markdown_text)
MD_EDITOR = {
    "RENDERER_CLASS": "myapp.renderers.MyRenderer",
}

Custom Upload Handler

from django_markdown_widget import BaseUploadHandler

class S3UploadHandler(BaseUploadHandler):
    def validate(self, file):
        # your validation logic
        pass

    def save(self, file) -> str:
        # save to S3 and return the URL
        return url
MD_EDITOR = {
    "UPLOAD_HANDLER_CLASS": "myapp.uploads.S3UploadHandler",
}

Development

git clone https://github.com/ganiyevuz/django-md-editor.git
cd django-md-editor
uv sync --group dev
make test
make lint

Run the example app:

cd example
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

License

MIT License. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_markdown_widget-1.0.1.tar.gz (101.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_markdown_widget-1.0.1-py3-none-any.whl (37.8 kB view details)

Uploaded Python 3

File details

Details for the file django_markdown_widget-1.0.1.tar.gz.

File metadata

File hashes

Hashes for django_markdown_widget-1.0.1.tar.gz
Algorithm Hash digest
SHA256 da217f033872feda7aed7147e3f9d738719b1966cddfd58ad37cfcf1b4fa3f02
MD5 4867d4c6553c50aaf95f608d26392be7
BLAKE2b-256 cf7c8c0ea327b7e43c723c0d41219871a38c71aa5a6315f2266af059356864d5

See more details on using hashes here.

File details

Details for the file django_markdown_widget-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_markdown_widget-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 749aed0e93d65c3902e23319f752cf79b0d5d3fcb2b677efa6fc2327c9cf8e7a
MD5 b6135b4090e1ba32eccca595ae0009c3
BLAKE2b-256 328e72dab0c0e23fbf1860013124a2c37351c0e71a83868e64436e9c13a5636c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page