naritoブログ

【お知らせ】
新ブログができました。今後そちらで更新し、このサイトは更新されません(ウェブサイト自体は残しておきます)
このブログの内容に関してコメントしたい場合は、新ブログのフリースペースに書き込んでください

このブログの内容を新ブログに移行中です。このブログで見つからない記事は、新ブログにありま

Django、よく使うフィルタ

約1110日前 2016年6月12日14:02
プログラミング関連
Django Python
Dangoでよく使うフィルタについてです。

例えば、以下のようなModelがあったとします。
タイトルとテキストだけを持つ、シンプルなModelです。

class POST(models.Model):
""" ブログのポスト """

title = models.CharField(max_length=255)
text = models.TextField()

def __str__(self):
return self.title


管理画面での入力は、以下のようにします。ここに入力した内容が、ブログの記事として表示されていきます。


これをテンプレートにて以下のように表示しても、基本的には思った結果にはなりません。

{{ post.text }}


post.textの中身は、以下のようになります。
HTMLで表示するためには、改行を<br>にする必要がありますね。

ここは\\nブログの本文\\nです


Djangoには便利なフィルタがあります。
今回のような場合は、以下のようにすると解決するでしょう。

{{ post.text | linebreaksbr }}


linebreaksbrですが、これはプレーンテキストの改行を HTML の改行 (<br />) タグに変換します。
つまり、上のpost.textは...

ここは<br />ブログの本文<br />です

という表示になります。これは非常によく使います。

また、<p>タグで囲みたいという場合ならば、以下です。

{{ post.text | linebreaks }}


結果は、以下のようになります。(見やすいよう改行しました)

<p>
ここは<br>
ブログの本文<br>
です
</p>


更に、URLがあったら<a>タグに変換したい、なんてときはこうします。

{{post.text | urlize | linebreaksbr }}


フィルタを自作することもできます。
https://torina.top/detail/240/

タグは便利なものが色々とあるので、見てみるとよいでしょう。
https://docs.djangoproject.com/ja/1.9/ref/templates/builtins/
http://docs.djangoproject.jp/en/latest/ref/templates/builtins.html


もしも、post.textに直接HTMLを書きたい、ということならば、
以下のように、autoescape off とするか

{% autoescape off %}
{{ post.text }}
{% endautoescape %}


もしくは、safeを使います。これは変数個々に対応できます。

{{ post.text | safe}}

このように、HTMLを書いていくことができます。


pythonコードで以下のようなものはよく使いますが、

for index, value in enumerate(data):


djangoのテンプレートでは、以下のようになります。

{% for value in data %}
{{ forloop.counter }}.{{ value }}<br>
{% endfor %}


urlizeで困るのは、単純なaタグにしか変換をしないことです。
例えば、' target='_blank'やrel='nofollow'が欲しい場合もあります。
その場合は、以下のようなフィルタを作ると良いでしょう。

@register.filter(is_safe=True)
def url_target_blank(text):
return text.replace('<a ', '<a target="_blank" rel="nofollow" ')


以下のように使います。urlizeの後に書きましょう。

{{ post.text | urlize | url_target_blank }}


このブログでは、以下のようなフィルタを使用しています。
実際に使う際は、endの後の半角スペースを消してください。

import re

from django import template
from django.utils.html import escape
from django.utils.safestring import mark_safe, SafeData
...
...

@register.filter(is_safe=True, needs_autoescape=True)
def blog(value, autoescape=True):
"""[spam]ham[end ]のような文字列を、適切なHTMLタグに変換する"""
autoescape = autoescape and not isinstance(value, SafeData)
if autoescape:
value = escape(value)
filters = re.finditer(r'\[filter (?P<tag_name>.*?)\].*?\[end\]', value)
results = []
for f in filters:
filter_name = f.group('tag_name')
origin_text = f.group()
filter_function = globals().get(filter_name)
if filter_function and callable(filter_function):
result_text = filter_function(origin_text)
else:
result_text = origin_text
results.append((origin_text, result_text))

for origin_text, result_text in results:
if origin_text != result_text:
value = value.replace(origin_text, result_text)
return mark_safe(value)


これは、例えば投稿画面で以下のように書くと、


以下のように出力されます。


filter spamの、spam部分が関数として存在すれば、その関数を呼び出すという仕組みです。
spamという関数がなければ、そのまま何もせず出力されます。
現在ブログで使っているのは、以下のような関数です。

def img(text):
"""http://spam.png等の画像URLを、imgタグに置換する"""

patterns = [r"http.*?jpg", r"http.*?png", r"http.*?jpeg", r"http.*?gif"]
for ptn in patterns:
href = re.search(ptn, text)
if href:
# Bootstrap4
return '<a href="{0}" target="_blank" rel="nofollow"><img src="{0}" class="img-fluid"/></a>'.format(href.group())
return text


def cord(text):
"""<pre>コード</pre>に置き換える"""

text = text.replace('<br />', '\n')
text = text.replace('', '').replace('[end ]', '')
# google-code-prettify
tag = '<pre class="prettyprint linenums">{}</pre>'.format(text)
return tag


def quote(text):
"""<blockquote>文字</blockquote>に置き換える"""

text = text.replace('[filter quote]', '').replace('[end ]', '')
# Bootstrap4
tag = '<blockquote class="blockquote"><p>{}</p></blockquote>'.format(
text)
return tag


def midasi1(text):
"""<span class="midasi1">文字</span>に置き換える

.midasi1 {
border-left: 10px solid rgb(197,219,238);
border-bottom: 1px solid rgb(197,219,238);
margin-top: 26px;
padding-left: 7px;
padding-bottom: 4px;
}
"""
text = text.replace('[filter h2]', '').replace('[end ]', '')
tag = '<p class="midasi1">{0}</p>'.format(text)
return tag


他にも、こんな関数も有用です。
これはfilter htmlのように使い、その部分だけはHTMLとして出力します。

import html.parser
html_parser = html.parser.HTMLParser()
...
...
def html(text):
"""[filter html]your_html[ end]を、そのままHTMLとして解釈する"""

text = text.replace('<br />', '\n')
text = text.replace('[filter html]', '').replace('[ end]', '')
tag = html_parser.unescape(text)
return tag


このhtml関数を使うと、urlizeと衝突がおきるかもしれません。
その場合は以下のように、filter urlも作成してしまいます。
内部でurlizeを呼び出し、_blankなんかを付け加えるだけです。

from django.template.defaultfilters import urlize
...
...
def url(text):
"""[filter url]http://...[ end]を、aタグして解釈する"""

text = text.replace('<br />', '\n')
text = text.replace('[filter url]', '').replace('[ end]', '')
tag = urlize(text)
tag = tag.replace('<a ', '<a target="_blank" rel="nofollow" ')
return tag