Django
テンプレートタグとフィルタ
サーチ…
カスタムフィルタ
フィルタを使用すると、変数に関数を適用できます。この関数は0または1の引数をとることができます。構文は次のとおりです。
{{ variable|filter_name }}
{{ variable|filter_name:argument }}
これは完全に有効なので、フィルタは連鎖することができます:
{{ variable|filter_name:argument|another_filter }}
Pythonに翻訳された場合、上記の行は次のようになります:
print(another_filter(filter_name(variable, argument)))
この例では、Model(インスタンスまたはクラス)またはverbose_name
に適用されるカスタムフィルタverbose_name
をverbose_name
します。引数がTrue
設定されている場合は、モデルの冗長な名前、またはその冗長な名前の複数形が返されTrue
。
@register.filter
def verbose_name(model, plural=False):
"""Return the verbose name of a model.
`model` can be either:
- a Model class
- a Model instance
- a QuerySet
- any object refering to a model through a `model` attribute.
Usage:
- Get the verbose name of an object
{{ object|verbose_name }}
- Get the plural verbose name of an object from a QuerySet
{{ objects_list|verbose_name:True }}
"""
if not hasattr(model, '_meta'):
# handle the case of a QuerySet (among others)
model = model.model
opts = model._meta
if plural:
return opts.verbose_name_plural
else:
return opts.verbose_name
単純なタグ
カスタムテンプレートタグを定義する最も簡単な方法は、 simple_tag
を使用することsimple_tag
。これらはセットアップするのが非常に簡単です。関数名はタグ名になりますが、引数はトークン(引用符で囲まれたスペースを除いて空白で区切られた "トークン")になります。キーワード引数もサポートしています。
私たちの例を説明する無駄なタグがあります:
{% useless 3 foo 'hello world' foo=True bar=baz.hello|capfirst %}
foo
とbaz
次のようなコンテキスト変数としましょう:
{'foo': "HELLO", 'baz': {'hello': "world"}}
私たちは、このようなレンダリングに非常に役に立たないタグを使いたいとしましょう:
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
引き数連結の種類は3回繰り返されます(3が最初の引き数です)。
タグの実装は次のようになります。
from django.utils.html import format_html_join
@register.simple_tag
def useless(repeat, *args, **kwargs):
output = ';'.join(args + ['{}:{}'.format(*item) for item in kwargs.items()])
outputs = [output] * repeat
return format_html_join('\n', '{}<br/>', ((e,) for e in outputs))
format_html_join
では、安全なHTMLとして<br/>
をマークすることができますが、 outputs
の内容はマークしません。
Nodeを使用した高度なカスタムタグ
場合によっては、 filter
やsimple_tag
に対して複雑すぎることもあります。これを実行すると、コンパイル関数とレンダラーを作成する必要があります。
この例では、テンプレートタグverbose_name
を次の構文で作成します。
例 | 説明 |
---|---|
{% verbose_name obj %} | モデルの冗長な名前 |
{% verbose_name obj 'status' %} | フィールド "ステータス"の冗長な名前 |
{% verbose_name obj plural %} | 複数のモデルの冗長な名前 |
{% verbose_name obj plural capfirst %} | 複数のモデルを大文字にした冗長な名前 |
{% verbose_name obj 'foo' capfirst %} | フィールドの大文字と小文字の名前 |
{% verbose_name obj field_name %} | 変数からのフィールドの詳細な名前 |
{% verbose_name obj 'foo'|add:'_bar' %} | フィールド "foo_bar"の冗長な名前 |
単純なタグでこれを行うことができない理由は、 plural
とcapfirst
は変数でも文字列でもなく、 "キーワード"であるということです。明らかにそれらを文字列'plural'
と'capfirst'
として渡すことができますが、これらの名前のフィールドと競合する可能性があります。 {% verbose_name obj 'plural' %}
は、「複数のobj
詳細な名前」または「 obj.plural
詳細な名前」をobj.plural
ますか?
まずコンパイル関数を作成しましょう:
@register.tag(name='verbose_name')
def do_verbose_name(parser, token):
"""
- parser: the Parser object. We will use it to parse tokens into
nodes such as variables, strings, ...
- token: the Token object. We will use it to iterate each token
of the template tag.
"""
# Split tokens within spaces (except spaces inside quotes)
tokens = token.split_contents()
tag_name = tokens[0]
try:
# each token is a string so we need to parse it to get the actual
# variable instead of the variable name as a string.
model = parser.compile_filter(tokens[1])
except IndexError:
raise TemplateSyntaxError(
"'{}' tag requires at least 1 argument.".format(tag_name))
field_name = None
flags = {
'plural': False,
'capfirst': False,
}
bits = tokens[2:]
for bit in bits:
if bit in flags.keys():
# here we don't need `parser.compile_filter` because we expect
# 'plural' and 'capfirst' flags to be actual strings.
if flags[bit]:
raise TemplateSyntaxError(
"'{}' tag only accept one occurrence of '{}' flag".format(
tag_name, bit)
)
flags[bit] = True
continue
if field_name:
raise TemplateSyntaxError((
"'{}' tag only accept one field name at most. {} is the second "
"field name encountered."
).format(tag_name, bit)
field_name = parser.compile_filter(bit)
# VerboseNameNode is our renderer which code is given right below
return VerboseNameNode(model, field_name, **flags)
レンダラー:
class VerboseNameNode(Node):
def __init__(self, model, field_name=None, **flags):
self.model = model
self.field_name = field_name
self.plural = flags.get('plural', False)
self.capfirst = flags.get('capfirst', False)
def get_field_verbose_name(self):
if self.plural:
raise ValueError("Plural is not supported for fields verbose name.")
return self.model._meta.get_field(self.field_name).verbose_name
def get_model_verbose_name(self):
if self.plural:
return self.model._meta.verbose_name_plural
else:
return self.model._meta.verbose_name
def render(self, context):
"""This is the main function, it will be called to render the tag.
As you can see it takes context, but we don't need it here.
For instance, an advanced version of this template tag could look for an
`object` or `object_list` in the context if `self.model` is not provided.
"""
if self.field_name:
verbose_name = self.get_field_verbose_name()
else:
verbose_name = self.get_model_verbose_name()
if self.capfirst:
verbose_name = verbose_name.capitalize()
return verbose_name