Skip to content

Django 集成 Editor 编辑器

1093字约4分钟

Pythoneditor.mdmarkdown

2025-05-19

Django 集成 Editor.md 编辑器

0.前提条件

为了更好的实现 Markdown 编辑器,这里使用了开源的 editor.md 用作页面上的 Markdown 编辑器,需要预先下载 Editor.md 的安装包到 Django 项目的静态文件目录下,如下所示

Django项目/
├── ../
├── static/
│   └── editor.md/
├── ../

放置完成后,可以访问 editor.md 下的 examples/simple.html ,如果能够出现 editor.md 的编辑器效果,说明 editor.md 位置放置正确,如下图所示。

image-20250519002253656

1.依赖配置安装

使用 pip 安装 markdown 扩展依赖。

pip install markdown

确认 Django 项目的 settings.py 是否有以下配置,如果缺少则进行补充。

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

STATIC_URL = '/static/

2.应用集成editor.md

修改要使用 markdown 编辑器的模型字段。

class ArticlePost(models.Model):
    ...
    body = models.TextField()  # 保存 Markdown 文本

修改要使用 markdown 编辑器的表单字段。

# 写文章的表单类
class ArticlePostForm(forms.ModelForm):
    class Meta:
        widgets = {
            'body': forms.Textarea(attrs={'id': 'id_content'}),
        }

配置 markdown 编辑器中上传图片的视图函数。

# 这一步存在问题,暂时不做实现
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse

@csrf_exempt
def upload_image(request):
    if request.method == 'POST' and request.FILES.get("editormd-image-file"):
        f = request.FILES["editormd-image-file"]
        today = datetime.now().strftime('%Y%m%d')
        path = os.path.join("uploads", "article", today, f.name)
        full_path = os.path.join(settings.MEDIA_ROOT, path)
        os.makedirs(os.path.dirname(full_path), exist_ok=True)

        with open(full_path, 'wb+') as destination:
            for chunk in f.chunks():
                destination.write(chunk)

        return JsonResponse({
            "success": 1,
            "message": "上传成功",
            "url": settings.MEDIA_URL + path
        })

    return JsonResponse({"success": 0, "message": "上传失败"})

配置 markdown 编辑器中上传图片的路由函数。

urlpatterns = [
    # markdown上传图片
    path('upload_image/', views.upload_image, name='upload_image'),
    path('success/', lambda r: HttpResponse("发布成功")),
]

模版中嵌入 Editor.md

<!-- 编辑器样式文件 -->
<head>
     <link rel="stylesheet" href="{% static 'editor.md/css/editormd.min.css' %}" />
</head>

<!-- 表单内容 -->
<form method="post" action="." class="my-4">
                {% csrf_token %}
                <!-- {{ article_post_form.media }}
                {{ article_post_form.as_p }}
                <button type="submit" class="btn btn-primary">文章发表</button> -->
                <div class="mb-3">
                    <label for="title" class="form-label">文章标题</label>
                    <input type="text" class="form-control" id="title" name="title" placeholder="请输入标题">
                  </div>
                  <div class="mb-3">
                    <label for="category" class="form-label">文章分类</label>
                    <select class="form-select" id="category" name="category">
                        {% for category in categories %}
                            <option value="{{ category.id }}">{{ category.name }}</option>
                        {% endfor %}
                    </select>
                  </div>

                  <div class="mb-3">
                    <label for="tag" class="form-label">文章标签</label>
                    <select multiple class="form-select" id="tag" name="tag">
                      {% for tag in tags %}
                            <option value="{{ tag.id }}">{{ tag.name }}</option>
                        {% endfor %}
                    </select>
                    <small class="form-text text-muted">按住 Ctrl (Windows) 或 ⌘ (Mac) 选择多个标签</small>
                  </div>

                <div id="editor">
                    <textarea name="body" id="id_content" style="display:none;">{{ form.content.value }}</textarea>
                </div>
                <button type="submit" class="btn btn-primary">提交</button>
            </form>
<!-- 这里一定要引用jquery,否则可能会导致无法加载编辑器 -->
<script src="{% static 'js/jquery-3.7.1.min.js' %}"></script>
<script src="{% static 'editor.md/editormd.min.js' %}"></script>

    <script>
        var editor = editormd("editor", {
            width: "100%",
            height: 500,
            path: "{% static 'editor.md/lib/' %}",
            imageUpload: true,
            imageFormats: ["jpg", "jpeg", "png", "gif", "bmp", "webp"],
            imageUploadURL: "{% url 'article:upload_image' %}", 
        });
    </script>

访问 markdown 编辑器的页面,如下图所示,说明 markdown 编辑器前端集成成功。

image-20250519011413938

点击提交,正常不报错,后端集成成功,点击新增加的文章,能够查看识别到的markdown文章目录信息,应用集成editor.md 正常无报错。

image-20250519011522516

3.markdown 上传图片

使用 editor.md 调用上传图片的接口会存在跨域和iframe问题,这里需要额外做一些设置。

  1. settings.py 中添加 X-Frame-Options 配置:

    # 允许同源iframe加载
    X_FRAME_OPTIONS = 'SAMEORIGIN'
  2. 修改 upload_image 视图函数,添加跨域的响应头:

    @csrf_exempt
    def upload_image(request):
        if request.method == 'POST' and request.FILES.get("editormd-image-file"):
            f = request.FILES["editormd-image-file"]
            today = datetime.now().strftime('%Y%m%d')
            path = os.path.join("uploads", "article", today, f.name)
            full_path = os.path.join(settings.MEDIA_ROOT, path)
            os.makedirs(os.path.dirname(full_path), exist_ok=True)
    
            with open(full_path, 'wb+') as destination:
                for chunk in f.chunks():
                    destination.write(chunk)
    
            response = JsonResponse({
                "success": 1,
                "message": "上传成功",
                "url": settings.MEDIA_URL + path
            })
            # 添加跨域响应头
            response["Access-Control-Allow-Origin"] = "*"
            response["Access-Control-Allow-Headers"] = "*"
            return response
    
        return JsonResponse({"success": 0, "message": "上传失败"})
  3. 修改前端模版集成编辑器的配置代码,重点是添加最后两行的代码,如下所示:

    var editor = editormd("editor", {
        width: "100%",
        height: 500,
        path: "{% static 'editor.md/lib/' %}",
        imageUpload: true,
        imageFormats: ["jpg", "jpeg", "png", "gif", "bmp", "webp"],
        imageUploadURL: "{% url 'article:upload_image' %}",
        crossDomainUpload: false,  // 设置为false,因为我们在同一域名下
        uploadCallbackURL: "",     // 不需要回调URL
    });
  4. 由于修改了 Django 项目的配置文件,这里需要重启 Django 项目,重启后访问页面,能够正常上传图片。

    image-20250519012540376

    点击确定后,markdown 编辑器界面能够正常展示上传的图片,如下所示:

    image-20250519012613328