DjangoBrothers BLOG

2019/06/26

このエントリーをはてなブックマークに追加
Python Django

【Django】 HTMLタグ、MarkDown エディタで投稿できるようにする

Djangoでテキストを投稿するときに、HTMLタグやMarkDown記法で書けるようにする方法です。

こんなアプリがあったとします。

models.py

class Blog(models.Model):
    title = models.CharField(max_length=40)
    text = models.TextField()

views.py

def index(request, id):
    blog = Blog.objects.get(id=id)
    return render(request, 'blog.html', {"blog": blog})

blog.html

<h1>{{ blog.title }}</h1>

<div>
    {{ blog.text }}
</div>

admin画面ではこのように表示され、テキストを入力することができます。

入力されたテキストはこのように表示されますが、当然文字の大きさや色を変えて装飾することはできません。

文字を装飾するためには、HTMLやMarkDownの記法で入力します。

HTMLタグで入力できるようにする

文字をHTMLタグで囲って保存してみます。

すると、タグがエスケープされて、画面にはHTMLタグがそのまま表示されてしまいます。。。

HTMLタグとして読み込むためには、safeというテンプレートタグを使います。

blog.html

<h1>{{ blog.title }}</h1>

<div>
    {{ blog.text |safe }}
</div>

これでHTMLタグを読み込めるようになりました。classやidを付与することで、cssを適用することもできます。

また、以下のように特殊文字を使うことでHTMLタグをそのまま画面に表示させることもできます。

MarkDownで入力できるようにする

HTMLタグで書く方法でも良いのですが、いちいちタグで囲っていくのはかなり面倒です。より楽にできるようにMarkDownでも書けるような設定をしてみます。

Python-MarkDownパッケージを使います。これは、MarkDown記法で書かれたものをHTML形式に変換できるパッケージです。細かい使い方はドキュメントにあります。

Python-Markdownの使い方

# インストール
$ pip install markdown==3.1.1

# MarkDownで書かれたものをHTMLに変換する
$ python3
>>> from markdown import markdown
>>> markdown('#見出し1')
'<h1>見出し1</h1>'

>>> markdown('テキストです')
'<p>テキストです</p>'

>>> markdown('[リンクです](http://sample.com)')
'<p><a href="http://sample.com">リンクです</a></p>' 

このように、markdown( )関数の引数にMarkDown形式で書いた文字列を渡すと、HTML形式に変換して返してくれます。また、HTMLを引数に取った場合は、そのままHTMLを返します。

HTMLを引数にした場合

>>> markdown('<h1>見出し1</h1>')
'<h1>見出し1</h1>' 

Djangoで使ってみる

markdown( )関数をmodels.pyで使ってみます。これによって、MarkDownで入力されたテキストを、HTMLに変換した形でテンプレート(blog.html)に渡すことができます。

models.py

from django.db import models
from markdown import markdown  # 追加

class Blog(models.Model):
    title = models.CharField(max_length=40)
    text = models.TextField()

    def get_markdown_text_as_html(self):
        """MarkDown記法で書かれたtextをHTML形式に変換して返す"""
        return markdown(self.text)

blog.html

<h1>{{ blog.title }}</h1>

<div>
    {{ blog.get_markdown_text_as_html | safe }}
</div>

これで、MarkDownで入力できるようになりました。入力した文字はHTMLに変換されて読み込まれます。

id名、class名を付与する

extensions=['attr_list']とすることで、idやclassなどを指定することができます。詳しい書き方はこちら。 これにより、MarkDownで書かれたものにもCSSを適用することができます。

Python-Markdownでid名、class名を付与する方法

>>> markdown('## 見出し2 {#title-2}', extensions=['attr_list'])
'<h2 id="title-2">見出し2</h2>'

>>> markdown('文章です\n{.text}', extensions=['attr_list'])
'<p class="text">文章です</p>'

>>> markdown('[リンク](sample.com){.link}', extensions=['attr_list'])
'<p><a class="link" href="sample.com">リンク</a></p>'

hタグにid, classを付与する場合は見出しの後ろに1つスペースを入れる必要があります。

pタグのようなブロック要素の後には改行が必要なので\nと入れています。admin画面上では\nと入れなくても、改行して1行下に{.text}と書いてあげれば大丈夫です。

GitHubっぽくcodeを書く

Python-MarkDownではデフォルトで、スペース4つかタブ1つをつけることによって、コードとみなされます。

codeタグに変換する

>>> markdown("    print('hello')")
"<pre><code>print('hello')\n</code></pre>"

admin画面でも、同じようにスペースを空けることでコードとして書くことができます。

スペースを空けるのではなく、GitHubのようにコードを```で囲みたいときはextensionsfenced_codeを追加します。

models.py

from django.db import models
from markdown import markdown

class Blog(models.Model):
    title = models.CharField(max_length=40)
    text = models.TextField()

    def get_markdown_text_as_html(self):
        """MarkDown記法で書かれたtextをHTML形式に変換して取得する"""
        return markdown(self.text,  extensions=['fenced_code', 'attr_list'])  # fenced_codeを追加

admin画面でこのように入力できるようになる

```python
print('hello')
```

HTMLに変換される

<pre><code class="Python">print('hello')</pre></code>

コードをハイライトする

コードと認識した部分をハイライトしたい場合は、highlight.jsというライブラリを使うと便利です。<pre>タグと<code>タグで囲まれた部分をいい感じにハイライトしてくれます。

使い方はHTMLファイルでこのように読み込むだけです。

highlight.jsを読み込む

<h1>{{ blog.title }}</h1>

<div>
    {{ blog.get_markdown_text_as_html | safe }}
</div>

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.8/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.8/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

外部パッケージで拡張する

fenced_codeのようにデフォルトでサポートされているもの以外でも、外部パッケージをインストールすればよりGitHubに近い書き方ができるようになります。

SHARE ! Tweet