Django Admin 整合 EasyMDE(markdown 編輯器)
models.py
content_html 用來存放 EasyMDE 從 content 產生的 HTML,這樣做有兩個好處。可以快取產生的 HTML,另外是格式不會跑掉。用其他 markdown 轉 HTML 格式會稍微與 EasyMDE 產生的 HTML 不一樣,要精確排版就會很頭痛。
class Post(models.Model):
content = models.TextField("內容")
content_html = models.TextField(blank=True, null=True) # 自動轉成 HTML 儲存
widgets.py
from django import forms
from django.utils.safestring import mark_safe
class EasyMDEWidget(forms.Textarea):
class Media:
css = {
'all': ('https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css',
'post.css')
}
js = (
'https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js',
)
def render(self, name, value, attrs=None, renderer=None):
textarea = super().render(name, value, attrs, renderer)
return mark_safe(f"""
{textarea}
<script>
document.addEventListener("DOMContentLoaded", function() {{
const easyMDE = new EasyMDE({{ element: document.getElementById("id_{name}") }});
const textarea = document.getElementById('id_content');
const form = textarea.closest('form');
form.addEventListener('submit', function () {{
const html = easyMDE.options.previewRender(easyMDE.value());
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const cleanHtml = doc.body.innerHTML;
const htmlField = document.getElementById('id_content_html');
if (htmlField) {{
htmlField.value = cleanHtml.trim();
}} else {{
alert('no id_content_html found')
}}
}});
}});
</script>
""")
form
class PostAdminForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
widgets = {
'content': EasyMDEWidget(),
'content_html': forms.HiddenInput(),
}
admin.py
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
form = PostAdminForm
post.css 有需要可以調整一下 EasyMDE 顏色
/* 編輯器本體 */
.EasyMDEContainer .CodeMirror {
background-color: #1e1e1e;
color: #e0e0e0;
}
/* 工具列背景與按鈕 */
.editor-toolbar {
background-color: #2a2a2a !important;
border: 1px solid #444;
}
/* 工具列 icon */
.editor-toolbar a {
color: #ccc !important;
}
.editor-toolbar li, .editor-toolbar button {
color: #ccc !important;
}
/* 工具列 icon hover 效果 */
.editor-toolbar a:hover {
background-color: #3a3a3a !important;
color: white !important;
}
/* Side-by-Side 預覽 */
.editor-preview-side {
background-color: #1e1e1e;
color: #e0e0e0;
border-left: 1px solid #444;
}
/* 全螢幕預覽 */
.editor-preview {
background-color: #1e1e1e;
color: #e0e0e0;
}
/* 全螢幕模式強制覆蓋 */
.CodeMirror-fullscreen,
.editor-toolbar.fullscreen {
background-color: #1e1e1e !important;
color: #e0e0e0 !important;
}
/* 工具列在全螢幕時 */
.editor-toolbar.fullscreen {
border-bottom: 1px solid #444;
}
/* CodeMirror 滾動條背景 */
.CodeMirror-scrollbar-filler,
.CodeMirror-gutter {
background-color: #1e1e1e;
}
/* 被啟用的按鈕(例如全螢幕、Side-by-Side) */
.editor-toolbar a.active,
.editor-toolbar a.active:hover {
background-color: #444 !important;
color: #fff !important;
}
/* 全螢幕狀態下的工具列也要套用暗色 */
.editor-toolbar.fullscreen a.active {
background-color: #444 !important;
color: #fff !important;
}
/* 在 Side by Side 或全螢幕模式下,稍微往右移 */
.CodeMirror.cm-s-easymde.CodeMirror-fullscreen {
margin-left: 10px !important;
}
/* Side by Side 模式編輯區向右推一點 */
.editor-preview-side {
margin-left: 10px !important;
}
/* 針對 EasyMDE / CodeMirror 編輯器游標改成白色 */
.CodeMirror-cursor {
border-left: 2px solid white !important;
}