%}のような、HTMLや他のタグを間に挟めるendタグを持つカスタムタグを自作する方法です。初Django(というかpythonも少ししか書いたことない)なのでお作法とか違ったらすいません。ドキュメントは下記ですが少々内容が足りないです。タグの引数の解析の説明がありません。https://docs.djangoproject.com/en/4.2/howto/custom-template-tags/#parsing-until-another-block-tagデフォルトタグのソースを見るのが手っ取り早いと思います。https://github.com/django/django/blob/stable/4.2.x/django/template/defaulttags.pyこの記事では以下のようなことが理解できると思います。カスタムブロックタグの書き方、登録の仕方。登録したタグを他のAPPで使う方法。contextへの新しい変数の代入とその掃除方法。コードと解説タグの登録app直下にtemplatetagsディレクトリを作り__init__.pyとmy_tags.py(名前は任意)を置き、my_tags.pyでタグを登録します。outputタグの登録は@register.tagでします。@registerには幾つかの登録メソッドがありますが、下記の回答にまとめてる人がいて分かりやすかったです。https://stackoverflow.com/questions/76246817/register-filter-vs-register-simple-tag-vs-register-tag-vs-register-inclusiondo_foobarはテンプレートに書かれたタグを解析してtemplate.Nodeの子クラスを返します。基本的なコードなのでブロックの中身だけ解析してFoobarNodeに渡し、返してます。parser.parse(("endfoobar",))でendfoobarまでのテンプレートに書いてあるnodeの行を集めます。endfoobarの直前までを集めてるので、まだendfoobarタグは解析前の状態で残っているためparser.delete_first_token()でendfoobarのタグを捨てます。tag?のようなエラーになります。FoobarNodeにrender関数を定義して、そこでHTMLテキストを返すと、それがテンプレートにレンダリングされます。返すHTMLテキストですがforのソースを見てください。https://github.com/django/django/blob/ce44eaf6d0b0df253fb4e632dfd1554c3f1461c3/django/template/defaulttags.py#L245django.utils.safestring.SafeStringを返してるのがわかると思います。'<div>foobar</div>'としてもそのまま出力されました。一応他のソースに倣いdjango.utils.safestring.SafeStringを返しておいた方が無難だと思います(ちなみにself.nodelist.renderの返り値はSafeStringです)。これをテンプレートでloadすれば使えます。%}</body></html>別のappでも使いたい別のappでも使えるようにするにはsettings.pyで登録してやる必要があります。%}タグの解析ビルトインのタグを見ると色々引数を渡せますが、自作タグにも当然渡せます、引数はテキストの配列でくるので解析が割と面倒です。タグにテンプレート変数とテキストの引数を渡してみましょう。%}これで下記のようなHTMLがレンダリングされる実装してみようと思います。<div>テキスト:foo</div><div>テキスト:bar</div>タグに書いた引数はdo_foobarの2番目の引数tokenに入ってきます。これはdjango.template.base.Tokenのインスタンスです。"'テキスト'"]となります。valuesがテキストで、"'テキスト'"はクオートで囲まれた状態できます。変数の解決はcontextにアクセスできるNodeでやりますのが、この関数ではparser.compile_filterを使って変数なのか、テキストなのか解析までやっておきます。parser.compile_filterの返り値はdjango.template.base.FilterExpressionです。mark_safe(''.join(outputs))contextを引数にresolveを呼ぶことで変数を解決できます。この変数名はform.emailのようなプロパティーへのアクセスもちゃんと解決できます。label_stringの方は文字列ですが、resolveを呼んでおくことで変数にも対応可能です。context.push():で囲います(pythonはブロックじゃなくてインデントなので囲うとは言わない?)。するとcontextに新しい空のdictが生成され、代入した変数はそこに登録、ブロックを抜けるとそのdictは削除されます。Masamoto以前は伝統芸能を生業としてましたが35才で転職。ウェブ更新のバイトから独学でソフトウェアエンジニアに。その会社を自社開発まで育て10年頑張りましたが今は起業しました。デザインは苦手ですがUI/UXからサーバー構築もサーバーサイドもフロントエンドも家事も料理もこなすフルスタック主夫です。バッジを贈って著者を応援しようバッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。バッジを贈るDiscussionMasamoto以前は伝統芸能を生業としてましたが35才で転職。ウェブ更新のバイトから独学でソフトウェアエンジニアに。その会社を自社開発まで育て10年頑張りましたが今は起業しました。デザインは苦手ですがUI/UXからサーバー構築もサーバーサイドもフロントエンドも家事も料理もこなすフルスタック主夫です。Proよくある質問LinksX(Twitter)GitHubメディアキットLegal利用規約プライバシーポリシ。