wagtail markdown field を使ってみる


Blog の body 部は、markdown で書きたいので、Mezzanine の mgaitan/mezzanine-pagedown: Fork from https://bitbucket.org/akhayyat/mezzanine-pagedown のような plugin が wagtail にもあるのか調べてみました。
結果を記載します。


前提

以下の環境で作業は実施しています。

  • OS

    % sw_vers
    ProductName:    Mac OS X
    ProductVersion: 10.13.1
    BuildVersion:   17B1003
    

  • Python の Version

    % python3 -V
    Python 3.6.2
    

  • wagtail の Version

    % python3 -m pip list --format=columns | grep wagtail
    wagtail                           1.13.1  
    

  • wagtail プロジェクトについて

    wagtail start mysite
    
    mysite というプロジェクトを作っている想定で、パスは記載します。

  • Blog アプリケーションについて
    wagtail Your first Wagtail site を参考に、Blog を作る | Monotalk で Blog を作成しています。


ライブラリ

wagtail markdown で検索するとすぐ見つかりますが、torchbox/wagtail-markdown: Markdown support for Wagtail を使用すれば、markdown 形式で body部の記載ができそうです。
Blog の body 部を model で MarkdownField として定義して使う、もしくは、StreamField という wagtail の API で使用する形になります。
今回は、MarkdownField を使用してみます。1


インストール、設定

インストール

PermissionError: [Errno 13] Permission denied: '/usr/local/bin/markdown_py' になったので、--user をつけてインストールします。

python3 -m pip install wagtail-markdown --user

INSTALLED_APPS に追加

base.py に、wagtailmarkdown を追加します。

  • mysite/mysite/settings/base.py
    INSTALLED_APPS = [  
        ... 
        'wagtailmarkdown',
        ...
    }
    

model に MarkdownFiled を追加

models.py の BlogPage の body 部 を MarkdownField に変更しました。

  • mysite/blog/models.py
    # Create your models here.
    from wagtail.wagtailcore.models import Page
    from wagtail.wagtailcore.fields import RichTextField
    from wagtail.wagtailadmin.edit_handlers import FieldPanel
    from wagtail.wagtailsearch import index
    
    from wagtail.api import APIField
    
    from wagtailmarkdown.edit_handlers import MarkdownPanel
    from wagtailmarkdown.fields import MarkdownField
    
    class BlogIndexPage(Page):
        intro = RichTextField(blank=True)
    
        content_panels = Page.content_panels + [
            FieldPanel('intro', classname="full")
        ]
    
    
    class BlogPage(Page):
        date = models.DateField("Post date")
        intro = models.CharField(max_length=250)
        body = MarkdownField(blank=True)
    
        search_fields = Page.search_fields + [
            index.SearchField('intro'),
            index.SearchField('body'),
        ]
    
        content_panels = Page.content_panels + [
            FieldPanel('date'),
            FieldPanel('intro'),
            MarkdownPanel("body", classname="full"),
        ]
    
        api_fields = [
            APIField('date'),
            APIField('intro'),
            APIField('body'),
        ]
    

Template の修正

Template で markdown を parse します。
{% load wagtailmarkdown %} を追加し、body を markdown で filter します。
* mysite/blog/templates/blog/blog_page.html

{% extends "base.html" %}

{% load wagtailcore_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
    <h1>{{ page.title }}</h1>
    <p class="meta">{{ page.date }}</p>
    <div class="intro">{{ page.intro }}</div>
    {{ page.body|markdown }}
    <p><a href="{{ page.get_parent.url }}">Return to blog</a></p>
{% endblock %}

Migration を実行

body の 型を変更したので、Migration を実行します。

python3 manage.py makemigrations
python3 manage.py migrate   

サーバー起動して確認

変更後の結果を確認します。

python3 manage.py runserver

wagtail の admin にログインして、Blog ページを開くと以下のように表示されました。
bodyの入力欄が大きくなっています。
"Login"

Markdown形式で入力してプレビュー

以下のような、Markdown を入力し、プレビューすると、"input"
以下のように表示されます。 [TOC] は効かないようです。
"preview"

Code HighLight する

Pygments の CSS を拝借して、base.htmlにコピー&ペーストしました。
CSS ファイルは Pygments Syntax Highlighter CSS Theme Files からダウンロードし、highlightを、codehiliteに置換しています。

  • mysite/mysite/templates/base.html
        <style>
            .codehilite code, .codehilite pre {
                color: #fdce93;
                background-color: #3f3f3f;
            }
    
            .codehilite .hll {
                background-color: #222;
            }
    
            .codehilite .err {
                color: #e37170;
                background-color: #3d3535;
            }
    
            .codehilite .k {
                color: #f0dfaf;
            }
    
            .codehilite .p {
                color: #41706f;
            }
    
            .codehilite .cs {
                color: #cd0000;
                font-weight: 700;
            }
    
            .codehilite .gd {
                color: #cd0000;
            }
    
            .codehilite .ge {
                color: #ccc;
                font-style: italic;
            }
    
            .codehilite .gr {
                color: red;
            }
    
            .codehilite .go {
                color: gray;
            }
    
            .codehilite .gs {
                color: #ccc;
                font-weight: 700;
            }
    
            .codehilite .gu {
                color: purple;
                font-weight: 700;
            }
    
            .codehilite .gt {
                color: #0040D0;
            }
    
            .codehilite .kc {
                color: #dca3a3;
            }
    
            .codehilite .kd {
                color: #ffff86;
            }
    
            .codehilite .kn {
                color: #dfaf8f;
                font-weight: 700;
            }
    
            .codehilite .kp {
                color: #cdcf99;
            }
    
            .codehilite .kr {
                color: #cdcd00;
            }
    
            .codehilite .ni {
                color: #c28182;
            }
    
            .codehilite .ne {
                color: #c3bf9f;
                font-weight: 700;
            }
    
            .codehilite .nn {
                color: #8fbede;
            }
    
            .codehilite .vi {
                color: #ffffc7;
            }
    
            .codehilite .c, .preview-zenburn .codehilite .g, .preview-zenburn .codehilite .cm, .preview-zenburn .codehilite .cp, .preview-zenburn .codehilite .c1 {
                color: #7f9f7f;
            }
    
            .codehilite .l, .preview-zenburn .codehilite .x, .preview-zenburn .codehilite .no, .preview-zenburn .codehilite .nd, .preview-zenburn .codehilite .nl, .preview-zenburn .codehilite .nx, .preview-zenburn .codehilite .py, .preview-zenburn .codehilite .w {
                color: #ccc;
            }
    
            .codehilite .n, .preview-zenburn .codehilite .nv, .preview-zenburn .codehilite .vg {
                color: #dcdccc;
            }
    
            .codehilite .o, .preview-zenburn .codehilite .ow {
                color: #f0efd0;
            }
    
            .codehilite .gh, .preview-zenburn .codehilite .gp {
                color: #dcdccc;
                font-weight: 700;
            }
    
            .codehilite .gi, .preview-zenburn .codehilite .kt {
                color: #00cd00;
            }
    
            .codehilite .ld, .preview-zenburn .codehilite .s, .preview-zenburn .codehilite .sb, .preview-zenburn .codehilite .sc, .preview-zenburn .codehilite .sd, .preview-zenburn .codehilite .s2, .preview-zenburn .codehilite .se, .preview-zenburn .codehilite .sh, .preview-zenburn .codehilite .si, .preview-zenburn .codehilite .sx, .preview-zenburn .codehilite .sr, .preview-zenburn .codehilite .s1, .preview-zenburn .codehilite .ss {
                color: #cc9393;
            }
    
            .codehilite .m, .preview-zenburn .codehilite .mf, .preview-zenburn .codehilite .mh, .preview-zenburn .highlight .mi, .preview-zenburn .codehilite .mo, .preview-zenburn .codehilite .il {
                color: #8cd0d3;
            }
    
            .codehilite .na, .preview-zenburn .codehilite .nt {
                color: #9ac39f;
            }
    
            .codehilite .nb, .preview-zenburn .codehilite .nc, .preview-zenburn .codehilite .nf, .preview-zenburn .codehilite .bp, .preview-zenburn .codehilite .vc {
                color: #efef8f;
            }
        </style>
    

markdown にcodeを書くと、以下のように表示されます。
インデントがうまく効いていないかもしれません。
"code"

font-awesome のリンクを追加する

後日気づいたのですが、ブログ投稿画面の、Icon リンクが表示されていませんでした。
Icon リンクの表示には、font-awesome の css の読み込みが必要になります。
wagtail には、Hooks — Wagtail 1.13.1 documentation という機能があり、wagtail_hooks.py という python スクリプトを作成し、拡張ポイントを記述することで、管理画面のカスタマイズが行えます。
Template 自体のカスタマイズもできるようですが、リンクを追加する程度であれば、Hooks を使うことで実現できました。
投稿画面でのみ適用できればよいので、insert_editor_css に対して、font-awesome のリンクを追加しました。

  • wagtail_hooks.py
    from django.utils.html import format_html
    from wagtail.wagtailcore import hooks
    
    @hooks.register('insert_editor_css')
    def editor_css():
        return format_html('<link rel="stylesheet" href="{}">', "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css")     
    

修正、Migration ともに、そこまでたいへんではありませんでした。
以上です。


  1. StreamField のドキュメントを見た限りは、Model層に影響を与えずに使用できる機能に思いました。StreamField blocks API · wagtail/wagtail Wiki 

コメント