Django:Templateから独自関数を呼び出して利用する方法
Django:Templateから独自関数を呼び出して利用する方法
Templateに渡した引数をPython独自関数に投げて値を返してもらい、表示を変更する。
こんな記事を見かけ、「へぇ~そんなことできるんだ」と思いやってみました。
下準備
テンプレートで独自関数を使うためにはいくつかの準備が必要です。
- アプリケーションディレクトリ直下に【templatetags】というディレクトリを作成
- templatetagsディレクトリに load 呼出し名.pyのファイルを作成 ※ここにコードを記載していきます。
- templatetagsディレクトリに__init__.pyを配置 ※ 何も書かなくてもOKな様です。
- templatetags内を設定したらDjangoを再起動 ※Dockerの場合はコンテナリビルドでもOKです。
後はTemplateで呼び出し用コードを記載すれば機能します。
templatetagsというディレクトリ名についてはDjangoの命名規制だそうです。
なので、配置場所とディレクトリ名は固定です。
コードの書き方について
- from django import template をUseする
- おまじない [ register = template.Library() ] を記載する
- 関数名を [ @register.filter(name="関数名") ] として記載する
- 関数は普通に [ def 関数名 ] で書き始める
- return は1つだけ ※ Template内でリストをこね回せないので
サンプルコード
■app\templatetags\tag_library.py
from django import template
from math import modf
register = template.Library()
@register.filter(name="change_Range")
def change_Range(date_range):
# 経過年を算出
fst = date_range/365
# 整数と少数に分ける decimal=少数
decimal, year = modf(fst)
# 経過年を差し引いた経過月を算出
scd = (decimal*365)/(365/12)
decimal, month = modf(scd)
val = str(round(year))+' 年 '+str(round(month))+' か月'
return val
独自関数の呼出しの手順
- 独自関数を使うTemplateに {% load 呼出し名 %}を配置
- 値を返す位置に {{ 引数| 関数名:第2引数 }} を記載
必要なのはこの2手だけです。
呼出しのサンプル
■app\template\index.html ※views.pyからSkillsという引数でDataFrameを渡している
{% extends "base_site.html" %}
(中略)
{% load tab_library %}
{% block content %}
<div class="right_col" role="main">
{% for index,sk in Skills.items %}
<div class="col-md-6 col-sm-12">{{index}}</div>
<div class="col-md-6 col-sm-12">{{ sk|change_Range }}</div>
{% endfor %}
<div>
Skills (DataFrame)
MySQL 2036
JavaScript 1976
PHP 1974
Python 1914
こんなコードで帰ってきます。
(前略※Forループ内だけ記載)
<div class="col-md-6 col-sm-12">MySQL</div>
<div class="col-md-6 col-sm-12">5 年 6 か月</div>
<div class="col-md-6 col-sm-12">JavaScript</div>
<div class="col-md-6 col-sm-12">5 年 4 か月</div>
<div class="col-md-6 col-sm-12">PHP</div>
<div class="col-md-6 col-sm-12">5 年 4 か月</div>
<div class="col-md-6 col-sm-12">Python</div>
<div class="col-md-6 col-sm-12">5 年 2 か月</div>
これはこれで面白い。でも新たな悩みが出来ました。
JavaScriptと独自関数の使い分けって何を基に考えればいい?
【TemplateSyntaxError '呼出し名' is not a registered tag library】が発生したら
Djangoからtemplatetagsディレクトリが見えていない時、このエラーが発生します。
そんな時は下の4点を確認しましょう。
- templatetagsディレクトリにスペルミスはないか
- templatetagsディレクトリはアプリ直下に配置されているか
- loadするファイルにregisterアノテーション(@register)を付けているか
- Djangoの再起動はしたか
私の場合はDockerコンテナのリビルドをしたら解消しました。
なので、ディレクトリを作成してpyファイルをソコに納めてもDjangoが認識してくれない事があるのは確実です。
また調べていた時に「INSTALLED_APPSにパスを通す必要がある」との情報がありましたが、コレは無くても大丈夫なようです。
(私の環境では記載したら500エラーが発生しました。)
独自関数は同一ファイル内にいくつでも格納できる
from django import template
from math import modf
register = template.Library()
""" {{ 引数|change_Range }}で利用 """
@register.filter(name="change_Range")
def change_Range(date_range):
(中略)
return val
""" {{ 引数|change_R }}で利用 """
@register.filter(name="change_R")
def change_R(date_range):
(中略)
return val
@ragister.filter(name="hoge")とすれば {{ 引数|hoge }}で利用することになります。
こんな感じで複数登録しても全く問題ありません。
同じ事ならJavaScriptでもできるJSと独自関数の棲み分けは?
そう、同じことはJavaScriptでもできます。
JavaScriptと独自関数の違いと言えばTemplateに渡す引数の形。
JavaScriptではDataFrame型を渡されても処理できないので、リストに変更しておく必要があります。
js_data = [['MySQL', 2036], ['JavaScript', 1976], ['PHP', 1974], ['python', 1914]]
(前略)
<div class="row">
<div id="Range"></div>
</div>
<script>
var JSDATA = {{js_data|safe}}
JSDATA.forEach(function(element){
if (typeof ts == "undefined") {
ts = '<div class="col-md-6 col-sm-12">';
}else{
ts = ts + '<div class="col-md-6 col-sm-12">';
};
let fst = element[1]/365
//整数部分
let years = Math.floor(fst)
//小数点以下
let decimal = parseFloat("0."+(String(fst)).split(".")[1])
//月数の計算
let scd = (decimal*365)/(365/12)
//整数部分
let month = Math.floor(scd)
ts = ts + element[0];
ts = ts + '</div><div class="col-md-6 col-sm-12">';
ts = ts + years + ' 年 ' + month + ' カ月';
ts = ts + '</div>';
});
document.getElementById("Range").innerHTML = ts;
</script>
どうすみ分ける?
明確な分岐ポイントが少なく最適な棲み分け方についてはとても難しい議題です。
ただ1点、独自関数の方は返却できる値が1つに限られるという点が挙げられます。
※複数の値を返すことはできるが受け取り(展開)が面倒
JS側は今回の例では 『id=Range』に対して『ひとまとまり』にしたコードを返却しましたが『class=Range』にすれば回数と合わせて複数の値を適用することができます。
なので、Template側でforループを使っていてもJavaScriptが使えないなんてことはありません。
個人的にはこんな棲み分けで考えています。
- returnで複数の値を扱いたい時は【JavaScript】
- DataFrameで渡す必要があるときは【独自関数】
- デザイン的な動作を考慮する場合は【JavaScript】
- スクリプトの統一性を考えるなら【独自関数】
- 他のシステムでも流用したいパーツであれば【JavaScript】
- CRUDに絡むパーツであれば【独自関数】
- 計算結果をもとにもう一度モデルにアクセスしたい【独自関数】
でもまぁ、ここら辺は人それぞれかなと思います。
今回使って面白かった関数
少数を整数部分と少数点以下の部分に分ける関数があるというのは知りませんでした。
「少数を小数点以下切り捨てして変数に格納して、それをもとの少数から減算して」というルートで実装しようとしていたので「まさか1行で出来るとは!」という感じでした。
いやぁ~、知らないだけで色々あるんですよね。
ちなみに【Round】いつも忘れて調べます。検索結果が出てきて「コレ!」ってわかるんだからいい加減に脳内書庫から「スッ」と取り出したいものだと強く思います。
まとめ
Templateを中心に考えると操作しやすいのは独自関数の方になると思います。
で、汎用性で考えるとJavaScriptかなと。


