CharFiledにつっこんだテキストを@やハッシュタグに対応するためにカスタムテンプレートフィルターをつくった

models.CharFiledは勝手にテキストノードっぽい感じでつっこんでくれる

だからエスケープとか考えなくていいけれど、その代わり逆にinnerHTML的にあるテキストに対して書き換える動作をしたかった。そのためにはカスタムテンプレートを自作する必要があった。

テンプレートのフィルタとは

{{ text|urlize }}みたいにあるテキストに対してURLを勝手に展開してくれる便利フィルタ。これを自作する。

基本的な構造

@atasatamatara @ dev:~/skz2_proj$ tree
.
├── README
├── __init__.py
├── config.py
├── data_skz.db
├── manage.py
├── settings.py
├── skz2
│   ├── __init__.py
│   ├── admin.py
│   ├── context_processors.py
│   ├── models.py
│   ├── templatetags
│   │   ├── __init__.py
│   │   └── cust_filter.py
│   ├── tests.py
│   ├── tools.py
│   ├── urls.py
│   └── views.py
│   
├── templates
│   ├── base.html
│   ├── index.html
│   └── skz2.html
└── urls.py

こんな感じでtemplatetagsを作って、__init__.pyと任意のカスタムフィルター.pyをつくってやる

中身

from django import template
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

import re
register = template.Library()

@register.filter
def user_mention(value, autoescape=None):
    """@で始まるユーザーをリンクにする"""
    user = re.search(r'@([\w]+)', value)
    if user:
        value= re.sub(r'@[\w]+', '<a href=https://twitter.com/#!/' + user.group(1) +'>@'+ user.group(1)+'</a>', value)
    return mark_safe(value)
user_mention.needs_autoescape = True

@register.filter
def hashtag(value, autoescape=None):
    """日本語ハッシュタグを含めたハッシュタグをリンクにする"""
    hashtag = re.search(r'#([\w一-龠ぁ-んァ-ヴー]+)'.decode('utf-8'), value)
    #こっちのほうがユニコードを扱うときにはいいかもしれない
    #hashtag = re.search(ur'#([\w一-龠ぁ-んァ-ヴー]+)', value)
    if hashtag:
        value= re.sub(r'#([\w一-龠ぁ-んァ-ヴー]+)'.decode('utf-8'), '<a href=https://twitter.com/#!/search/%23' + hashtag.group(1) +'>#'    + hashtag.group(1)+'</a>', value)
        #value= re.sub(ur'#([\w一-龠ぁ-んァ-ヴー]+)', '<a href=https://twitter.com/#!/search/%23' + hashtag.group(1) +'>#'    + hashtag.group(1)+'</a>', value)
    return mark_safe(value)
hashtag.needs_autoescape = True

今回は動的にinnerHTML的に書き換えてやる必要があったのでmark_safeをつかってautoescape=None, needs_autoescape=True にしたけれど、そんなに破壊的なことをやらなければもっと単純にできるはず。

追記

mark_safeとかneeds_autoescape = True とかはなんかなくても動いたような……そこらへんは適宜自分で判断してください。

使うとき

ここでハマった。Loadする必要がある

{% load cust_filter %}

あとは普通にフィルターとして使う

{{ obj.text|urlize|user_mention|hashtag }}

以上

知れば簡単。知らないとハマる。それだけのこと。

蛇足

日本語ハッシュタグの認識するために[一-龠ぁ-んァ-ヴー]をコピペ的に使ってたのだけど素直に動かなかった。 .decode('utf-8') したら動いた。内部でutf-8で動いてくれてるかと思ったら想定外に動いてなかったりするから、ちゃんと decode しないといけない。

追記

@tokibito
「r'#([\w一-龠ぁ-んァ-ヴー]+)'.decode('utf-8')」は、
「ur'#([\w一-龠ぁ-んァ-ヴー]+)'」とUnicodeリテラルにすればいけそうな気がするがどうかな?

素直に u"" で囲んだ方が素直ですね……。
thx!!