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

- Python -
2020.12.31
Django

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

今回、以下2つのパッケージを使って実現しました。

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

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

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

DjangoでMarkdown

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

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

mdeditorの公式デモサイトがあるので、実際のエディタの感じを試してみてください。
Markdown editor 動作デモ ≫

mdeditorとMarkdownインストール時に参考にしたリンク:

django-mdeditor:github: pylixm/django-mdeditor

Markdown:Render the markdown in django

※実装時の環境:

  • MacOS Catalina ver10.15.3
  • Python: 3.7.4
  • Django: 3.0

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

  1. mdeditorパッケージをpipインストール
  2. mdeditorの設定
  3. マイグレーションの実行

ここまで行うと、Markdownエディタで編集できる画面が整います。

1. mdeditorパッケージをインストール

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

pip install django-mdeditor

2. mdeditorの設定

インストール完了後、settings.pyに以下を追記。

settings.pyINSTALLED_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文など、個人の現在の環境ごとに適宜読み替えて、必要部分を修正してください。

urls.pyfrom 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で編集できるようにしたいとき。

models.pyfrom django.db import models

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

この場合、以下のように修正します。

  1. 2行目:MDTextFieldをimportする
  2. 6行目:MDTextField()に書き換える
models.pyfrom django.db import models
from mdeditor.fields import MDTextField # 追加

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

models.pyの修正はこれだけです。

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

admin.pyfrom django.contrib import admin
from .models import Article

admin.site.register(Article)

3. マイグレーションの実行

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

python manage.py makemigrations

python manage.py migrate

マイグレーションに成功したら、django admin画面から編集画面を開いてみてください。以下のようにマークダウンエディタが表示されていれば成功です。

DjangoでMarkdown

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

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

このままdjango admin画面でのみMarkdownエディタを使うのであれば、この項目自体読み飛ばしてください。

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

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

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

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

forms.pyfrom mdeditor.fields import MDTextFormField # 追加

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

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

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

HTML{% 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側で特殊なコードは不要で、

10行目:{{ form.media }}

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

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

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

Markdown記法をフロントでHTML表示させる手順

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

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

DjangoでMarkdown

index.html等フロントの表示

上記例だと「##」はh2タグ、「###」はh3タグにHTMLとして変換されていないと使い物になりません。

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

  1. Markdownパッケージをpipインストール
  2. Markdownの設定

1. Markdownパッケージをインストール

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

pip install Markdown

2. Markdownの設定

アプリフォルダ内のtemplatesフォルダと並列させて、以下フォルダ/ファイルを作成します。

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

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

DjangoでMarkdown

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

markdown_extras.pyfrom 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に以下タグを追記します。

HTML{% load markdown_extras %}
...(省略)
{{ content|markdown|safe }}

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

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

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

DjangoでMarkdown

ここまで終わったところで表示処理まで完了・・・と言いたいのですが、一部フロント表示がリアルタイムプレビューと比べると寂しい部分があります。

mdeditorリアルタイムプレビュー通りのスタイリングでフロント表示する

このままでは以下コードハイライト部分などスタイリングが中途半端な状態になっているはずです。

djangoのマークダウン

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

DjangoでMarkdown

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

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

mdeditorパッケージインストール時に一緒に入ってきたCSSとJavaScriptを読み込ませるだけなので簡単です。

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

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

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

HTML  <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へ以下を貼り付け保存するだけで英語に変更できます。

settings.pyMDEDITOR_CONFIGS = {
    'default': {
        'language': 'en',
    }
}

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

番外編:Markdownの拡張

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

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

Djangoでマークダウン

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

Djangoでマークダウン

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

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

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

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

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

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

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

Djangoでマークダウン

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

DjangoでMarkdown

markdown_extras.pyを編集します。

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

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

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

↑TOP