【Django】Markdownエディタを実装する方法【簡単】

- Python -
2020.02.18
Django

Djangoでブログを自作しようとして、WordPressでお馴染みのWYSIWYGエディタかMarkdownエディタか悩んで、あえて使い慣れていないMarkdownにしてみることに。

思ったより簡単に実装できたんですが、日本語での説明が少なく「ここはどうしたらええねん!」っていうポイントで調べたこともありメモを残します。

実装後のMarkdownエディタの見た目はこんな風に仕上がります↓

DjangoでMarkdown

  1. リアルタイムプレビュー機能(左側に打ち込むと右側に反映される)
  2. コードハイライター機能
  3. ファイルアップロード機能

などなど、デフォルトで高機能なのでこれだけ導入すればかなりリッチな感じに仕上がります。

デモサイトがあるのでエディタの感じを試してみてください。
Open source online Markdown editor ≫

以下2つのパッケージを使って実現していきます。

  1. django-mdeditor -> Markdownエディタ本体パッケージ
  2. Markdown -> Markdown表記のHTML変換用パッケージ

※英語に抵抗が無い方は、以下ドキュメントを順番に参照した方が確実です↓

django-mdeditorインストール:github: pylixm/django-mdeditor

Markdownインストール:Render the markdown in django

mdeditorのスタイリングをフロントエンドにも反映させる方法:
stack overflow:How to render the markdown content in the front-end using django-mdeditor?

※実装時の環境:

  • Python: 3.7.4
  • Django: 3.0

DjangoでMarkdownエディタを実装する

mdeditorパッケージをインストール、設定

まずはmdeditorパッケージをインストールします。

pip install django-mdeditor

settings.pyに以下を追記。( ... 部分は、環境によって異なる部分であることを意図)

INSTALLED_APPS = [
    ...
    'mdeditor',
]

# これはDjangoバージョン3.0以上の場合のみ記載すること
X_FRAME_OPTIONS = 'SAMEORIGIN'

# ファイルアップロード用
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'

アップロードするファイルを格納する用のフォルダuploads/editorを作成します。他アプリフォルダと並列するように以下のようになります。

DjangoでMarkdown

プロジェクト本体のurls.pyに以下のように追記。import文など、個人の現在の環境ごとに適宜読み替えて、必要部分を修正します。

from django.conf.urls import url, include
from django.conf.urls.static import static
from django.conf import settings
...

urlpatterns = [
    ...
    url(r'mdeditor/', include('mdeditor.urls'))
]

if settings.DEBUG:
    # static files (images, css, javascript, etc.)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

models.pyを編集します。例えばモデルを以下のように作っていて、contentをmarkdownで編集したいとします。

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    # ↓ここのフィールドをMarkdownエディタに変えたい
    content = models.TextField()

この場合、contentの右辺をMDTextField()に書き換えます。

from django.db import models
from mdeditor.fields import MDTextField # 追加

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = MDTextField() # 変更

モデルクラスをadmin.pyへ登録し、django admin画面で表示確認ができるようにします。とはいえ、これはプロジェクト開始序盤にやっていることだと思うので、不要な場合が多いかと。

from django.contrib import admin
from .models import Article

admin.site.register(Article)

マイグレーションを実行します。

python manage.py makemigrations
python manage.py migrate

django admin画面から編集画面を開き、以下のようにMarkdownエディタが表示されていれば成功です。

DjangoでMarkdown

django admin画面でしか編集しない、という場合はエディター実装はここで完了です。

Formクラス、ModelFormクラスのフィールドにもMarkdownエディタを実装する場合

たとえばdjango admin画面ではなく xxx.com/post/create/ のようなURLで記事作成画面を用意して、ここでもMarkdownエディタで編集したい場合は追加での設定が必要です。

django.forms.ModelFormクラスでフォームを実装している場合は、Formクラスの修正は不要です。直下グレーボックス内の修正部分は読み飛ばし、templateの修正から始めてください。

★ django.forms.Formクラスの時のみ必要な修正 ★

まずは、Formクラスを編集します。以下のようにMarkdownエディタにしたいフィールドcontentの右辺をMDTextFormField()に変更。

from mdeditor.fields import MDTextFormField # 追加

class ArticleForm (forms.Form):
    title = forms.CharField ()
    content = MDTextFormField() # 変更

そして、formを表示するtemplate(xxx.html)の編集です。

以下、github公式から例をそのまま掲載。

{% load static %}
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <form method="post" >
            {% csrf_token %}
            {{ form.media }}
            {{ form.as_p }}
            <p><input type="submit" value="post"></p>
        </form>
    </body>
</html>

現在どういうテンプレートの書き方をしているかによって編集する箇所は変わるので適宜読み替えて欲しいのですが、mdeditorを導入するからといってtemplate側で特殊なコードは不要で、

{{ form.media }}

この1行を含めることだけに注意すれば大丈夫です。これが無いとMarkdownエディタ表示がおかしくなります。

フォームの出力は{{ form.as_p }}である必要はありません。{{ form.content }}のようにフィールド一つひとつを個別に出力していてもOK。

ここまでできたら、上記フォームを表示するviewへアクセスし、Markdownエディタ表示されていれば成功です。

Markdownで編集したコンテンツをtemplateへhtmlで表示させる

ここまでで、Markdownで編集ができるようになりました。

がしかし、このままでは編集したコンテンツを表示するページは以下のようにMarkdown記法の記号がそのまま出力されてしまう状態です。

DjangoでMarkdown

Markdown表記をHTMLへ変換させる設定を追加します。

Markdownをインストール、設定

まずはパッケージをインストール。

pip install Markdown

そして、アプリフォルダ内のtemplatesフォルダと並列させて、

  1. templatetagsフォルダを作成
  2. その配下に markdown_extras.pyファイルを作成

をします。

参考までに僕の場合、"blog"アプリでMarkdownを実装したかったので、以下のようにblog配下にtemplatetagsフォルダを作成しました。

DjangoでMarkdown

作成したmarkdown_extras.pyに以下を貼り付けます

from django import template
from django.template.defaultfilters import stringfilter

import markdown as md

register = template.Library()

@register.filter()
@stringfilter
def markdown(value):
    return md.markdown(value, extensions=['markdown.extensions.fenced_code'])

あとは、templateに以下タグを追記します。

{% load markdown_extras %}
...
{{ content|markdown|safe }}

contentだけは適宜読み替えてください。

参考までに、僕の場合は以下のようになっています。

これで、以下のようにMarkdown表記がHTMLへ変換された状態で表示されるようになります。

DjangoでMarkdown

・・・ところが、このままではコード部分などスタイリングが中途半端な状態になっています。

ここからはCSSやJSで個別にスタイリングすればOKといえばOKなのですが、「面倒だからとりあえずエディタのプレビュー通りにフロントでも表示させたい」という場合があるかと思います↓

DjangoでMarkdown

そんな時の方法を以下でご紹介。

ここからはmdeditorのgithub公式には説明書きがないので、stack overflowの回答から拝借します。

mdeditorプレビューのスタイリングをフロントにも適用する方法

mdeditorのCSSとJavaScriptを読み込ませるだけなので簡単です。

<head>タグ内で以下CSSファイルを読み込む。

  <link href="{% static 'mdeditor/css/editormd.min.css' %}" rel="stylesheet">
  <link href="{% static 'mdeditor/css/editormd.preview.css' %}" rel="stylesheet">

<body>閉じタグの直前で以下JavaScriptを読み込む。

  <script src="{% static 'mdeditor/js/jquery.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/editormd.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/marked.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/prettify.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/raphael.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/underscore.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/sequence-diagram.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/flowchart.min.js' %}"></script>
  <script src="{% static 'mdeditor/js/lib/jquery.flowchart.min.js' %}"></script>
<script>
  $(function () {
      editormd.markdownToHTML("content", {
          emoji           : true,
          taskList        : true,
          tex             : true, 
          flowChart       : true,
          sequenceDiagram : true,
      });
      $(".reference-link").each(function (i,obj) {
        console.log(obj)
      })
  })
</script>

これで、記事表示ページでも編集画面のプレビューと同じようなスタイルが適用されて表示されていれば完了です。

一部、中国語表記のため英語に変更する方法

普通に編集するぶんには気にならないのですが、たとえば、ファイルアップロードのボタンをクリックすると以下のように中国語表記になっている部分があります。

DjangoでMarkdown

これを英語に変更する方法です。

DjangoでMarkdown

settings.pyへ以下を貼り付け保存するだけで英語に変更できます。

MDEDITOR_CONFIGS = {
    'default': {
        'language': 'en',
    }
}

残念ながら"ja"などとしても日本語にはなりません。が、編集画面自体は言語に依存した表記はないので大きな問題にはならないかと。

以上、DjangoでMarkdownエディタを実装する方法でした。

番外編:Markdownの拡張

エディタ上で自動生成される目次をフロントにも表示させる

ここまでの通りにmdeditorを設定すると、エディタ上に[TOC](Table Of Contentsの略)と表記するだけでリアルタイムプレビュー上では自動的にhタグを元に目次を生成してくれるようになります。

Djangoでマークダウン

ところが、フロント表示では [TOC] という文字列が表示されるだけになってしまいます。

Djangoでマークダウン

これを、フロントでもしっかり目次として表示されるように拡張させます。ものすごく簡単です。

Markdownをインストール/設定パートで作成した、markdown_extras.pyへ追記します。

...
def markdown(value):
    return md.markdown(value, extensions=['markdown.extensions.fenced_code', 'toc']) # 末尾に 'toc' を追加

リストの末尾に'toc'を追記しただけです。

これで、自動生成された目次がフロントでもリアルタイムプレビューと同様に表示されるようになります。

tableをフロントにも表示させる

デフォルトでは、以下のようにリアルタイムプレビューにtableが表示されつつも、

Djangoでマークダウン

フロント表示ではこのようになってしまいます。

DjangoでMarkdown

markdown_extras.pyを編集します。

...
def markdown(value):
    return md.markdown(value, extensions=['markdown.extensions.fenced_code', 'tables']) # 末尾に 'tables' を追加

リストの末尾に'tables'を追記しただけです。

これで、フロントでもリアルタイムプレビューと同様にテーブルが表示されるようになります。