Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

TemplateLoader自作のススメ

http://d.hatena.ne.jp/jbking/20060310/p1
で困ってるようですがTemplate_Loaderを自作してみたらどう?って話です。
でいつもの通りQuickHack。
(ソース読めの領域です)

TEMPLATE_LOADERSの簡単な仕様

Template_Loaderは"settings.py"に書くわけだけど
デフォルトでは

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

となっています。
確か上から順にloaderを適用していき該当のTemplateを探します。

django.template.loaders.app_directories.load_template_source:抜粋
・・・・
def get_template_sources(template_name, template_dirs=None):
    for template_dir in app_template_dirs:
        yield os.path.join(template_dir, template_name) + settings.TEMPLATE_FILE_EXTENSION
・・・・

dir内のファイルをかったぱしから漁る仕様になっています。
ここにもあるように拡張子は

settings.TEMPLATE_FILE_EXTENSION

となっています。
で実際の値はというと・・・・
django.conf.g;obal_settings:抜粋

# Extension on all templates.
TEMPLATE_FILE_EXTENSION = '.html'

となっており".html"のみをtemplateとして扱う仕様になっています。
うーん、基本的にDjangoのtemplateはhtmlに依存していないので(タグとか見ていない)もっといろんな事に使いたいよね。

LOADERの自作

で自作。
元ネタがあるのでそこだけちょいといじればいいんでない?

自作ローダーjisaku.py
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.template import TemplateDoesNotExist
import os

app_template_dirs = []
for app in settings.INSTALLED_APPS:
    i = app.rfind('.')
    if i == -1:
        m, a = app, None
    else:
        m, a = app[:i], app[i+1:]
    try:
        if a is None:
            mod = __import__(m, '', '', [])
        else:
            mod = getattr(__import__(m, '', '', [a]), a)
    except ImportError, e:
        raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0])
    template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
    if os.path.isdir(template_dir):
        app_template_dirs.append(template_dir)

app_template_dirs = tuple(app_template_dirs)

def get_template_sources(template_name, template_dirs=None):
    for template_dir in app_template_dirs:
#        yield os.path.join(template_dir, template_name) + settings.TEMPLATE_FILE_EXTENSION
        yield os.path.join(template_dir, template_name) + settings.HOGEHOGE_FILE_EXTENSION

def load_template_source(template_name, template_dirs=None):
    for filepath in get_template_sources(template_name, template_dirs):
        try:
            return (open(filepath).read(), filepath)
        except IOError:
            pass
    raise TemplateDoesNotExist, template_name
load_template_source.is_usable = True

自Project内の"template""templates"って名前のDir配下を読みに行きます。

今回は自分のProjectの"settings.py"に

# Extension on all templates.
HOGEHOGE_FILE_EXTENSION = '.mail_template'

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
    #自作loaderを追加
    'jisaku.load_template_source',
)

を追加すれば使えるはずです。
もっと柔軟にしたければ

def get_template_sources(template_name, template_dirs=None):
    for template_dir in app_template_dirs:
#        yield os.path.join(template_dir, template_name) + settings.TEMPLATE_FILE_EXTENSION
        yield os.path.join(template_dir, template_name) 

として

        template_name = "%s/%s_form.%s" % ( 'hogehoge', 'comment','template' )
        t = template_loader.get_template( template_name )

のように拡張子込みでtemplateをゲットするのもありです。

拡張子がひとつな理由

ソースを見てなんとなーく感じただけですが

load処理抜粋
def load_template_source(template_name, template_dirs=None):
    for filepath in get_template_sources(template_name, template_dirs):
        try:
            return (open(filepath).read(), filepath)
        except IOError:
            pass
    raise TemplateDoesNotExist, template_name
load_template_source.is_usable = True

見てもわかるように単純に開けるかどうかどうかを全ファイルに対して行う仕様です。
やっぱり意図しないファイルが開かないように推奨してんだろうなあと。
上記より不必要なTemplateLoaderは使わない方がパフォーマンス的によさげです。
(もちろんキャッシュ化はしてますけど。一回目がけっこー時間かかるかなと)
うくく。