django-markdown-editor

agusmakmunGPL-3.0活跃维护

给 Django 项目用的 Markdown 编辑器,兼容 Bootstrap、Semantic-UI 和 Tailwind CSS,支持图片上传和实时预览,省去自己拼装编辑器的麻烦。

🙌 Awesome Django Markdown Editor, supported for Bootstrap, Semantic-UI & Tailwind CSS

v1.8.5发布于 2026-10-31 15:33 UTC896star1,279fork同步于 2026-11-10 11:54 UTC

怎么上手

由 README 提炼,执行前请确认
pip install martor
硬件要求按 README 说明

README 未说明

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

Django 的 Markdown 编辑器插件,带实时预览,兼容 Bootstrap、Semantic-UI 和 Tailwind

给 Django 项目用的 Markdown 编辑器,兼容 Bootstrap、Semantic-UI 和 Tailwind CSS,支持图片上传和实时预览,省去自己拼装编辑器的麻烦。

核心能力

  • 集成 Ace Editor,提供实时预览
  • 支持 Bootstrap、Semantic-UI、Tailwind 三种主题
  • 图片可上传到 imgur 或自定义上传器,也能从剪贴板粘贴
  • 支持 @[username] 提及、emoji 和视频嵌入
  • 提供 MartorField、MartorFormField 和 AdminMartorWidget,可直接接入模型、表单和后台

容易踩的坑

安装后需把 'martor' 加入 INSTALLED_APPS 并在 urls.py 里 include('martor.urls'),还要执行 collectstatic;使用 imgur 上传需先注册应用获取 IMGUR_CLIENT_ID 和 IMGUR_API_KEY;模板里必须引入对应主题的 css/js(可用 form.media 自动加载),否则编辑器样式和功能不生效

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.8.5
  1. v1.8.5最新正式版2026-10-31 · 2026-10-31 15:33 UTC
  2. v1.8.42026-10-31 · 2026-10-31 14:58 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/agusmakmun/django-markdown-editor/compare/v1.8.3...v1.8.4

  3. v1.8.32026-10-31 · 2026-10-31 13:48 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/agusmakmun/django-markdown-editor/compare/v1.8.2...v1.8.3

  4. v1.8.22026-06-22 · 2026-06-22 15:01 UTC
    查看更新说明

    What's Changed

    New Contributors

    Full Changelog: https://github.com/agusmakmun/django-markdown-editor/compare/v1.8.1...v1.8.2

  5. v1.8.12026-04-18 · 2026-04-18 12:22 UTC
    查看更新说明

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    martor pypi version paypal donation

    license python version django version build black

    📖 Documentation: https://django-markdown-editor.readthedocs.io

    Martor is a Markdown Editor plugin for Django, supported for Bootstrap, Semantic-UI & Tailwind CSS.

    Features

    • Live Preview
    • Integrated with Ace Editor
    • Supported with Bootstrap, Semantic-UI and Tailwind CSS
    • Supported Multiple Fields fixed this issue
    • Upload Images to imgur.com (via API) and custom uploader, or paste them from the clipboard
    • Light & dark mode: follows Django admin's theme (OS setting or the theme toggle), and your own pages via CSS variables
    • Direct Mention users @[username] - (requires user to logged in).
    • Supports embed/iframe video from (Youtube, Vimeo, Dailymotion, Yahoo, Veoh, & Metacafe)
    • Spellchecking (only supports US English at this time)
    • Emoji :emoji_name: + Cheat sheets
    • Martor Commands Reference
    • Supports Django Admin
    • Toolbar Buttons
    • Highlight pre
    • Custom ID Attributes (Add custom IDs to any text element using {#custom-id} syntax, e.g., # Heading1 {#my-h1-id}, for easy linking and navigation.

    Preview

    editor

    preview

    Requirements

    • Django>=3.2
    • Markdown>=3.0
    • requests>=2.12.4
    • bleach

    Installation

    Martor is available directly from PyPI:

    1. Installing the package.

    $ pip install martor
    

    2. Don't forget to add 'martor' to your 'INSTALLED_APPS' setting (without migrations).

    # settings.py
    INSTALLED_APPS = [
        ....
        'martor',
    ]
    

    3. Add url pattern to your urls.py.

    # urls.py
    urlpatterns = [
        ...
        path('martor/', include('martor.urls')),
    ]
    

    4. Collect martor's static files in your STATIC_ROOT folder.

    ./manage.py collectstatic
    

    Setting Configurations settings.py

    Please register your application at https://api.imgur.com/oauth2/addclient to get IMGUR_CLIENT_ID and IMGUR_API_KEY.

    # Choices are: "semantic", "bootstrap", "tailwind"
    MARTOR_THEME = 'bootstrap'
    
    # Global martor settings
    # Input: string boolean, `true/false`
    MARTOR_ENABLE_CONFIGS = {
        'emoji': 'true',        # to enable/disable emoji icons.
        'imgur': 'true',        # to enable/disable imgur/custom uploader.
        'mention': 'false',     # to enable/disable mention
        'jquery': 'true',       # to include/revoke jquery (require for admin default django)
        'living': 'false',      # to enable/disable live updates in preview
        'spellcheck': 'false',  # to enable/disable spellcheck in form textareas
        'hljs': 'true',         # to enable/disable hljs highlighting in preview
    }
    
    # To show the toolbar buttons
    MARTOR_TOOLBAR_BUTTONS = [
        'bold', 'italic', 'horizontal', 'heading', 'pre-code',
        'blockquote', 'unordered-list', 'ordered-list',
        'link', 'image-link', 'image-upload', 'emoji',
        'direct-mention', 'toggle-maximize', 'help'
    ]
    
    # To setup the martor editor with title label or not (default is False)
    MARTOR_ENABLE_LABEL = False
    
    # Martor's admin and light/dark mode styles (default is True).
    # Disable when using custom admin interface e.g django-grappelli
    MARTOR_ENABLE_ADMIN_CSS = True
    
    # Imgur API Keys
    MARTOR_IMGUR_CLIENT_ID = 'your-client-id'
    MARTOR_IMGUR_API_KEY   = 'your-api-key'
    
    # Markdownify
    MARTOR_MARKDOWNIFY_FUNCTION = 'martor.utils.markdownify' # default
    MARTOR_MARKDOWNIFY_URL = '/martor/markdownify/' # default
    # Require login for the markdownify (preview) endpoint.
    MARTOR_MARKDOWNIFY_LOGIN_REQUIRED = False # default
    
    # CSRF cookie name used by the ajax requests, follows django's CSRF_COOKIE_NAME by default.
    MARTOR_CSRF_COOKIE_NAME = CSRF_COOKIE_NAME
    
    # Delay in milliseconds to update editor preview when in living mode.
    MARTOR_MARKDOWNIFY_TIMEOUT = 0 # update the preview instantly
    # or:
    MARTOR_MARKDOWNIFY_TIMEOUT = 1000 # default
    
    # Markdown extensions (default)
    MARTOR_MARKDOWN_EXTENSIONS = [
        'markdown.extensions.extra',
        'markdown.extensions.nl2br',
        'markdown.extensions.smarty',
        'markdown.extensions.fenced_code',
        'markdown.extensions.sane_lists',
    
        # Custom markdown extensions.
        'martor.extensions.urlize',
        'martor.extensions.del_ins',      # ~~strikethrough~~ and ++underscores++
        'martor.extensions.mention',      # to parse markdown mention
        'martor.extensions.emoji',        # to parse markdown emoji
        'martor.extensions.mdx_video',    # to parse embed/iframe video
        'martor.extensions.escape_html',  # to handle the XSS vulnerabilities
        "martor.extensions.mdx_add_id",  # to parse id like {#this_is_id}
    ]
    
    # Markdown Extensions Configs
    MARTOR_MARKDOWN_EXTENSION_CONFIGS = {}
    
    # Markdown urls
    MARTOR_UPLOAD_URL = '' # Completely disable the endpoint
    # or:
    MARTOR_UPLOAD_URL = '/martor/uploader/' # default
    
    MARTOR_SEARCH_USERS_URL = '' # Completely disables the endpoint
    # or:
    MARTOR_SEARCH_USERS_URL = '/martor/search-user/' # default
    
    # Markdown Extensions
    # MARTOR_MARKDOWN_BASE_EMOJI_URL = 'https://www.webfx.com/tools/emoji-cheat-sheet/graphics/emojis/'     # from webfx
    MARTOR_MARKDOWN_BASE_EMOJI_URL = 'https://github.githubassets.com/images/icons/emoji/'                  # default from github
    # or:
    MARTOR_MARKDOWN_BASE_EMOJI_URL = ''  # Completely disables the endpoint
    MARTOR_MARKDOWN_BASE_MENTION_URL = 'https://python.web.id/author/'                                      # please change this to your domain
    
    # If you need to use your own themed "bootstrap" or "semantic ui" dependency
    # replace the values with the file in your static files dir
    MARTOR_ALTERNATIVE_JS_FILE_THEME = "semantic-themed/semantic.min.js"   # default None
    MARTOR_ALTERNATIVE_CSS_FILE_THEME = "semantic-themed/semantic.min.css" # default None
    MARTOR_ALTERNATIVE_JQUERY_JS_FILE = "jquery/dist/jquery.min.js"        # default None
    
    # URL schemes that are allowed within links
    ALLOWED_URL_SCHEMES = [
        "file", "ftp", "ftps", "http", "https", "irc", "mailto",
        "sftp", "ssh", "tel", "telnet", "tftp", "vnc", "xmpp",
    ]
    
    # https://gist.github.com/mrmrs/7650266
    ALLOWED_HTML_TAGS = [
        "a", "abbr", "b", "blockquote", "br", "cite", "code", "command",
        "dd", "del", "dl", "dt", "em", "fieldset", "h1", "h2", "h3", "h4", "h5", "h6",
        "hr", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend",
        "li", "ol", "optgroup", "option", "p", "pre", "small", "span", "strong",
        "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul"
    ]
    
    # https://github.com/decal/werdlists/blob/master/html-words/html-attributes-list.txt
    ALLOWED_HTML_ATTRIBUTES = [
        "alt", "class", "color", "colspan", "datetime",  # "data",
        "height", "href", "id", "name", "reversed", "rowspan",
        "scope", "src", "style", "title", "type", "width"
    ]
    

    Check this setting is not set else csrf will not be sent over ajax calls:

    CSRF_COOKIE_HTTPONLY = False
    

    Usage

    Model
    from django.db import models
    from martor.models import MartorField
    
    class Post(models.Model):
        description = MartorField()
    
    Form
    from django import forms
    from martor.fields import MartorFormField
    
    class PostForm(forms.Form):
        description = MartorFormField()
    
    Admin
    from django.db import models
    from django.contrib import admin
    
    from martor.widgets import AdminMartorWidget
    
    from yourapp.models import YourModel
    
    class YourModelAdmin(admin.ModelAdmin):
        formfield_overrides = {
            models.TextField: {'widget': AdminMartorWidget},
        }
    
    admin.site.register(YourModel, YourModelAdmin)
    
    Template Renderer

    Simply safely parse markdown content as html output by loading templatetags from martor/templatetags/martortags.py.

    {% load martortags %}
    {{ field_name|safe_markdown }}
    
    # example
    {{ post.description|safe_markdown }}
    

    Don't miss to include the required css & js files before use. The easiest way is the widget media, which loads exactly the files for your MARTOR_THEME: {{ form.media }} on a form page, or pass MartorWidget().media to the template context. See martor_demo/app/templates/demo for a full example.

    Linking the files by hand works too, e.g. for MARTOR_THEME = 'bootstrap':

    {% extends "base.html" %}
    {% load static %}
    {% load martortags %}
    
    {% block css %}
      <link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'martor/css/martor.bootstrap.min.css' %}" type="text/css" media="all" rel="stylesheet" />
    {% endblock %}
    
    {% block content %}
      <div class="martor-preview">
        <h1>Title: {{ post.title }}</h1>
        <p><b>Description:</b></p>
        <hr />
        {{ post.description|safe_markdown }}
      </div>
    {% endblock %}
    
    {% block js %}
      <script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
      <script>
        $('.martor-preview pre').each(function(i, block){
            hljs.highlightBlock(block);
        });
      </script>
    {% endblock %}
    
    Template Editor Form

    Different with Template Renderer, the Template Editor Form have more css & javascript dependencies. The simplest is to render the form media, which includes everything for your MARTOR_THEME:

    <head>
      {{ form.media.css }}
    </head>
    <body>
      <form method="post">{% csrf_token %}{{ form.as_p }}<button>Save</button></form>
      {{ form.media.js }}
    </body>
    

    Or include them by hand:

    {% extends "base.html" %}
    {% load static %}
    
    {% block css %}
      <link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'martor/css/martor.bootstrap.min.css' %}" type="text/css" media="all" rel="stylesheet" />
    {% endblock %}
    
    {% block content %}
      <form class="form" method="post">{% csrf_token %}
        <div class="form-group">
          {{ form.title }}
        </div>
        <div class="form-group">
          {{ form.description }}
        </div>
        <div class="form-group">
          <button class="btn btn-success">
            <i class="save icon"></i> Save Post
          </button>
        </div>
      </form>
    {% endblock %}
    
    {% block js %}
      <script type="text/javascript" src="{% static 'plugins/js/ace.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/mode-markdown.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/ext-language_tools.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/theme-github.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/typo.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/spellcheck.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/emojis.min.js' %}"></script>
      <script type="text/javascript" src="{% static 'martor/js/martor.bootstrap.min.js' %}"></script>
    {% endblock %}
    
    Tailwind CSS Theme

    When using MARTOR_THEME = 'tailwind', include the required CSS and JavaScript files:

    Template Renderer (Tailwind):

    {% extends "base.html" %}
    {% load static %}
    {% load martortags %}
    
    {% block css %}
      <link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'plugins/css/tailwind.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'martor/css/martor.tailwind.min.css' %}" type="text/css" media="all" rel="stylesheet" />
    {% endblock %}
    
    {% block content %}
      <div class="container mx-auto px-4 py-8">
        <div class="martor-preview bg-white rounded-lg shadow-sm p-6">
          <h1 class="text-3xl font-bold text-gray-900 mb-4">{{ post.title }}</h1>
          <div class="prose prose-sm max-w-none">
            {{ post.description|safe_markdown }}
          </div>
        </div>
      </div>
    {% endblock %}
    
    {% block js %}
      <script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
      <script>
        document.querySelectorAll('.martor-preview pre code').forEach((block) => {
          hljs.highlightBlock(block);
        });
      </script>
    {% endblock %}
    

    Template Editor Form (Tailwind):

    {% extends "base.html" %}
    {% load static %}
    
    {% block css %}
      <link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'plugins/css/tailwind.min.css' %}" type="text/css" media="all" rel="stylesheet" />
      <link href="{% static 'martor/css/martor.tailwind.min.css' %}" type="text/css" media="all" rel="stylesheet" />
    {% endblock %}
    
    {% block content %}
      <div class="container mx-auto px-4 py-8">
        <form class="bg-white rounded-lg shadow-sm p-6" method="post">{% csrf_token %}
          <div class="mb-6">
            <label class="block text-sm font-medium text-gray-700 mb-2">{{ form.title.label }}</label>
            {{ form.title }}
          </div>
          <div class="mb-6">
            <label class="block text-sm font-medium text-gray-700 mb-2">{{ form.description.label }}</label>
            {{ form.description }}
          </div>
          <div class="flex justify-end">
            <button class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition-colors">
              Save Post
            </button>
          </div>
        </form>
      </div>
    {% endblock %}
    
    {% block js %}
      <script type="text/javascript" src="{% static 'plugins/js/ace.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/mode-markdown.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/ext-language_tools.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/theme-github.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/typo.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/spellcheck.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
      <script type="text/javascript" src="{% static 'plugins/js/emojis.min.js' %}"></script>
      <script type="text/javascript" src="{% static 'martor/js/martor.tailwind.min.js' %}"></script>
    {% endblock %}
    

    Light & Dark Mode

    In Django admin the editor, preview and modals follow the admin theme automatically (OS setting and the admin's theme toggle), for every MARTOR_THEME.

    On your own pages, martor uses the same CSS variable names as Django admin, so define them for your dark theme (light values are the fallback when they are not set):

    html[data-theme="dark"] {
      --body-bg: #17181c;          /* editor & preview background */
      --body-fg: #e5e7eb;          /* text */
      --body-quiet-color: #9aa1ab; /* inactive tabs, quotes */
      --body-loud-color: #ffffff;  /* headings */
      --darkened-bg: #1f2126;      /* code, table stripes, gutter */
      --border-color: #343740;
      --hairline-color: #26282e;
      --link-fg: #a99dff;
    }
    

    This needs MARTOR_ENABLE_ADMIN_CSS = True (default), which also applies the variables to rendered <div class="martor-preview">{{ post.description|safe_markdown }}</div> output. The demo (martor_demo/app/static/demo/) has a complete light/dark/auto toggle.

    Custom Uploader

    If you want to save the images uploaded to your storage, Martor also provides a way to handle this. Please checkout this WIKI

    Images pasted into the editor from the clipboard are uploaded through the same MARTOR_UPLOAD_URL (form field markdown-image-upload).

    Test Martor from this Repository

    Assuming you are already setup with a virtual environment (virtualenv):

    $ git clone https://github.com/agusmakmun/django-markdown-editor.git
    $ cd django-markdown-editor/ && pip install -e .
    $ cd martor_demo/
    $ python manage.py makemigrations && python manage.py migrate
    $ python manage.py runserver
    

    Checkout at http://127.0.0.1:8000/simple-form/ on your browser (the post form and admin need a user: python manage.py createsuperuser). Switch MARTOR_THEME in martor_demo/martor_demo/settings.py to try the other themes, and use the navbar toggle for light/dark mode.

    Documentation

    Complete documentation is available online at: https://django-markdown-editor.readthedocs.io/

    Running Documentation Locally

    To build and view the documentation locally:

    1. Clone the repository and set up your environment:

    $ git clone https://github.com/agusmakmun/django-markdown-editor.git
    $ cd django-markdown-editor/
    $ python -m venv venv
    $ source venv/bin/activate  # On Windows: venv\Scripts\activate
    

    2. Install documentation dependencies:

    $ pip install -r docs/requirements.txt
    

    3. Build the documentation:

    $ cd docs/
    $ sphinx-build -b html . _build/html
    

    Or using the included Makefile:

    $ cd docs/
    $ make html
    

    4. Open the documentation in your browser:

    $ open _build/html/index.html  # On macOS
    # Or on Linux/Windows, navigate to docs/_build/html/index.html
    

    Alternatively, serve the documentation with a local HTTP server:

    $ make serve
    # Then open http://localhost:8000 in your browser
    

    The documentation includes:

    • Installation and quickstart guides
    • Complete configuration reference
    • Usage examples for models, forms, widgets, and admin
    • API documentation
    • Troubleshooting and FAQ
    Contributing to Documentation

    The documentation is built with Sphinx and uses reStructuredText format. To contribute:

    1. Edit the .rst files in the docs/ directory
    2. Build the docs locally to test your changes
    3. Submit a pull request with your improvements

    Martor Commands Reference

    command reference

    Notes

    Martor was inspired by these great projects: django-markdownx, Python Markdown and Online reStructuredText editor.