フォームの操作
このドキュメントについて
このドキュメントでは、Webフォームの基本と、DjangoでのWebフォームの処理方法について紹介します。 フォームAPIの特定の領域の詳細については、フォームAPI 、フォームフィールド、およびフォームとフィールドの検証を参照してください。
コンテンツを公開するだけで、訪問者からの入力を受け入れないWebサイトやアプリケーションを構築することを計画していない限り、フォームを理解して使用する必要があります。
Djangoは、サイト訪問者からの入力を受け入れ、入力を処理して応答するためのフォームを作成するのに役立つさまざまなツールとライブラリを提供します。
HTMLフォーム
HTMLでは、フォームは<form>...</form>
内の要素のコレクションであり、訪問者がテキストの入力、オプションの選択、オブジェクトやコントロールの操作などを行って、その情報をサーバーに送り返すことができます。
これらのフォームインターフェイス要素の一部(テキスト入力またはチェックボックス)は、HTML自体に組み込まれています。 他のものははるかに複雑です。 日付ピッカーをポップアップしたり、スライダーを移動したり、コントロールを操作したりできるインターフェイスは、通常、JavaScriptとCSS、およびHTMLフォーム<input>
要素を使用してこれらの効果を実現します。
<input>
要素と同様に、フォームは2つのことを指定する必要があります。
- where :ユーザーの入力に対応するデータが返されるURL
- how :データが返されるHTTPメソッド
例として、Django管理者のログインフォームには、いくつかの<input>
要素が含まれています。ユーザー名用のtype="text"
、パスワード用のtype="password"
、type="submit"
「ログイン」ボタン。 また、ユーザーには表示されない非表示のテキストフィールドもいくつか含まれており、Djangoはこれを使用して次に何をするかを決定します。
また、フォームデータは<form>
のaction
属性で指定されたURL(/admin/
)に送信する必要があり、HTTPを使用して送信する必要があることもブラウザに通知します。 method
属性で指定されたメカニズム-post
。
<input type="submit" value="Log in">
要素がトリガーされると、データは/admin/
に返されます。
GETおよびPOST
GET
とPOST
は、フォームを処理するときに使用する唯一のHTTPメソッドです。
Djangoのログインフォームは、POST
メソッドを使用して返されます。このメソッドでは、ブラウザーがフォームデータをバンドルし、送信用にエンコードしてサーバーに送信し、応答を受信します。
対照的に、GET
は、送信されたデータを文字列にバンドルし、これを使用してURLを作成します。 URLには、データを送信する必要のあるアドレス、およびデータのキーと値が含まれています。 https://docs.djangoproject.com/search/?q=forms&release=1
の形式のURLを生成するDjangoドキュメントで検索を行うと、これが実際に動作していることがわかります。
GET
とPOST
は通常、さまざまな目的で使用されます。
システムの状態を変更するために使用できるすべての要求(たとえば、データベースを変更する要求)は、POST
を使用する必要があります。 GET
は、システムの状態に影響を与えない要求にのみ使用する必要があります。
GET
もパスワードフォームには適していません。パスワードはURLに表示されるため、ブラウザの履歴やサーバーログにもすべてプレーンテキストで表示されるためです。 大量のデータや、画像などのバイナリデータにも適していません。 管理フォームに対してGET
要求を使用するWebアプリケーションは、セキュリティリスクです。攻撃者がフォームの要求を模倣して、システムの機密部分にアクセスするのは簡単です。 POST
は、Djangoの CSRF保護などの他の保護と組み合わせることで、アクセスをより細かく制御できます。
一方、GET
は、GET
リクエストを表すURLを簡単にブックマーク、共有、または再送信できるため、Web検索フォームなどに適しています。
フォームでのDjangoの役割
フォームの処理は複雑なビジネスです。 Djangoの管理者について考えてみましょう。ここでは、いくつかの異なるタイプのデータの多数のアイテムをフォームに表示するために準備し、HTMLとしてレンダリングし、便利なインターフェイスを使用して編集し、サーバーに返して検証およびクリーンアップしてから、保存または渡す必要があります。さらなる処理のために。
Djangoのフォーム機能は、この作業の大部分を簡素化および自動化できます。また、ほとんどのプログラマーが自分で作成したコードで実行できるよりも安全に実行できます。
Djangoは、フォームに関連する作業の3つの異なる部分を処理します。
- レンダリングの準備をするためのデータの準備と再構築
- データのHTMLフォームの作成
- クライアントから送信されたフォームとデータを受信して処理する
これらすべてを手動で行うコードを書くことは可能ですが、Djangoがすべてを処理してくれます。
Djangoのフォーム
HTMLフォームについて簡単に説明しましたが、HTML <form>
は必要な機構の一部にすぎません。
Webアプリケーションのコンテキストでは、「フォーム」は、そのHTML <form>
、それを生成するDjango Form 、または送信時に返される構造化データを指す場合があります。これらのパーツのエンドツーエンドの作業コレクションに。
Django Form クラス
このコンポーネントシステムの中心にあるのは、Djangoの Form クラスです。 Djangoモデルがオブジェクトの論理構造、その動作、およびそのパーツの表現方法を記述するのとほぼ同じ方法で、 Form クラスはフォームを記述し、その動作と表示を決定します。
モデルクラスのフィールドがデータベースフィールドにマップされるのと同様の方法で、フォームクラスのフィールドはHTMLフォーム<input>
要素にマップされます。 ( ModelForm は、 Form を介してモデルクラスのフィールドをHTMLフォーム<input>
要素にマップします。これはDjango管理者が基づいているものです。)
フォームのフィールドはそれ自体がクラスです。 フォームデータを管理し、フォームが送信されたときに検証を実行します。 DateField と FileField は非常に異なる種類のデータを処理し、それを使用して異なることを行う必要があります。
フォームフィールドは、ブラウザ内のユーザーにHTML「ウィジェット」(ユーザーインターフェイス機構の一部)として表されます。 各フィールドタイプには適切なデフォルトのウィジェットクラスがありますが、これらは必要に応じてオーバーライドできます。
フォームのインスタンス化、処理、およびレンダリング
Djangoでオブジェクトをレンダリングする場合、通常は次のようにします。
- ビューでそれを取得します(たとえば、データベースから取得します)
- テンプレートコンテキストに渡します
- テンプレート変数を使用してHTMLマークアップに展開します
テンプレートでのフォームのレンダリングには、他の種類のオブジェクトのレンダリングとほぼ同じ作業が含まれますが、いくつかの重要な違いがあります。
データを含まないモデルインスタンスの場合、テンプレートでそれを使用して何かを行うことが役立つことはめったにありません。 一方、入力されていないフォームをレンダリングすることは完全に理にかなっています。これは、ユーザーに入力してもらいたいときに行うことです。
したがって、ビューでモデルインスタンスを処理する場合、通常はデータベースから取得します。 フォームを扱うときは、通常、ビューでインスタンス化します。
フォームをインスタンス化するときに、フォームを空のままにするか、次のように事前入力するかを選択できます。
- 保存されたモデルインスタンスからのデータ(編集用の管理フォームの場合のように)
- 他のソースから収集したデータ
- 以前のHTMLフォーム送信から受信したデータ
これらの最後のケースは、ユーザーがWebサイトを読むだけでなく、Webサイトに情報を送り返すことも可能にするため、最も興味深いものです。
フォームの作成
行う必要のある作業
ユーザーの名前を取得するために、Webサイトに簡単なフォームを作成するとします。 テンプレートには次のようなものが必要です。
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
これは、POST
メソッドを使用して、フォームデータをURL /your-name/
に返すようにブラウザに指示します。 「あなたの名前:」というラベルの付いたテキストフィールドと、「OK」とマークされたボタンが表示されます。 テンプレートコンテキストにcurrent_name
変数が含まれている場合、それはyour_name
フィールドに事前入力するために使用されます。
HTMLフォームを含むテンプレートをレンダリングし、必要に応じてcurrent_name
フィールドを提供できるビューが必要になります。
フォームが送信されると、サーバーに送信されるPOST
リクエストにフォームデータが含まれます。
次に、その/your-name/
URLに対応するビューも必要になります。このビューは、リクエスト内の適切なキーと値のペアを見つけて処理します。
これは非常に単純な形式です。 実際には、フォームには数十または数百のフィールドが含まれる場合があり、その多くは事前入力が必要な場合があり、ユーザーが操作を完了する前に編集-送信サイクルを数回実行することが予想されます。
フォームが送信される前であっても、ブラウザで検証を行う必要がある場合があります。 より複雑なフィールドを使用して、ユーザーがカレンダーから日付を選択するなどの操作を実行できるようにすることもできます。
この時点で、Djangoにこの作業のほとんどを実行させる方がはるかに簡単です。
Djangoでフォームを作成する
フォームクラス
HTMLフォームをどのように表示するかはすでにわかっています。 Djangoでの出発点は次のとおりです。
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
これは、単一のフィールド(your_name
)を持つ Form クラスを定義します。 フィールドに人間に優しいラベルを適用しました。これは、レンダリング時に<label>
に表示されます(ただし、この場合、指定した label は、実際には同じラベルです)。省略した場合は自動的に生成されます)。
フィールドの最大許容長は、 max_length で定義されます。 これは2つのことを行います。 HTML <input>
にmaxlength="100"
を配置します(したがって、ブラウザーは、ユーザーが最初にその数を超える文字を入力できないようにする必要があります)。 また、Djangoがブラウザからフォームを受信すると、データの長さを検証することも意味します。
Form インスタンスには is_valid()メソッドがあり、すべてのフィールドに対して検証ルーチンを実行します。 このメソッドが呼び出されたときに、すべてのフィールドに有効なデータが含まれていると、次のようになります。
True
を返します- フォームのデータを cleaned_data 属性に配置します。
初めてレンダリングしたときのフォーム全体は、次のようになります。
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>
には<form>
タグまたは送信ボタンが含まれていないことに注意してください。 それらをテンプレートで提供する必要があります。
景色
Django Webサイトに返送されるフォームデータは、ビュー、通常はフォームを公開したのと同じビューによって処理されます。 これにより、同じロジックの一部を再利用できます。
フォームを処理するには、フォームを公開するURLのビューでフォームをインスタンス化する必要があります。
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
GET
リクエストでこのビューに到達すると、空のフォームインスタンスが作成され、レンダリングされるテンプレートコンテキストに配置されます。 これは、URLに初めてアクセスしたときに発生すると予想されることです。
POST
リクエストを使用してフォームが送信された場合、ビューは再びフォームインスタンスを作成し、リクエストからのデータを入力します。form = NameForm(request.POST)
これは「フォームへのデータのバインド」と呼ばれます(これで、バインドされたフォームになります)。
フォームのis_valid()
メソッドを呼び出します。 True
でない場合は、フォームを使用してテンプレートに戻ります。 今回はフォームが空ではなくなったため( unbound )、HTMLフォームには以前に送信されたデータが入力され、必要に応じて編集および修正できます。
is_valid()
がTrue
の場合、検証済みのすべてのフォームデータをcleaned_data
属性で見つけることができるようになります。 このデータを使用して、データベースを更新したり、他の処理を行ってから、HTTPリダイレクトをブラウザーに送信して、次に進む場所を指示することができます。
テンプレート
name.html
テンプレートで多くのことを行う必要はありません。
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
すべてのフォームのフィールドとその属性は、Djangoのテンプレート言語によってそのテンプレート:Form
からHTMLマークアップに解凍されます。
フォームとクロスサイトリクエストフォージェリ保護
Djangoには、クロスサイトリクエストフォージェリに対する使いやすい保護が付属しています。 CSRF保護を有効にしてPOST
経由でフォームを送信する場合は、前の例のように:ttag: `csrf_token` テンプレートタグを使用する必要があります。 ただし、CSRF保護はテンプレート内のフォームに直接関連付けられていないため、このドキュメントの次の例ではこのタグを省略しています。
HTML5入力タイプとブラウザー検証
フォームに URLField 、 EmailField 、または任意の整数フィールドタイプが含まれている場合、Djangoはurl
、email
、number
を使用します] HTML5入力タイプ。 デフォルトでは、ブラウザはこれらのフィールドに独自の検証を適用できますが、これはDjangoの検証よりも厳しい場合があります。 この動作を無効にする場合は、form
タグにnovalidate
属性を設定するか、 TextInput などの別のウィジェットをフィールドに指定します。
これで、Django Form によって記述され、ビューによって処理され、HTML <form>
としてレンダリングされる作業用Webフォームができました。
始めるために必要なのはそれだけですが、フォームフレームワークはあなたの指先でもっと多くのものを提供します。 上記のプロセスの基本を理解したら、フォームシステムの他の機能を理解する準備をし、基盤となる機械についてもう少し学ぶ準備をする必要があります。
Django Form クラスの詳細
すべてのフォームクラスは、 django.forms.Form または django.forms.ModelForm のいずれかのサブクラスとして作成されます。 ModelForm
はForm
のサブクラスと考えることができます。 Form
とModelForm
は、実際には(プライベート)BaseForm
クラスから共通の機能を継承しますが、この実装の詳細が重要になることはめったにありません。
モデルとフォーム
実際、フォームを使用してDjangoモデルを直接追加または編集する場合、 ModelForm を使用すると、フォームが作成されるため、時間、労力、コードを大幅に節約できます。 Model
クラスの適切なフィールドとその属性。
バインドされたフォームインスタンスとバインドされていないフォームインスタンス
バインドされたフォームとバインドされていないフォームの違いは重要です。
- バインドされていないフォームには、関連付けられたデータがありません。 ユーザーにレンダリングすると、空になるか、デフォルト値が含まれます。
- バインドされたフォームはデータを送信したため、そのデータが有効かどうかを判断するために使用できます。 無効なバインドされたフォームがレンダリングされると、修正するデータをユーザーに通知するインラインエラーメッセージが含まれる可能性があります。
フォームの is_bound 属性は、フォームにデータがバインドされているかどうかを示します。
フィールドの詳細
上記の最小限の例よりも便利なフォームを検討してください。これを使用して、個人のWebサイトに「連絡先」機能を実装できます。
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
以前のフォームでは、your_name
、 CharField という単一のフィールドを使用していました。 この場合、フォームにはsubject
、message
、sender
、cc_myself
の4つのフィールドがあります。 CharField 、 EmailField 、および BooleanField は、使用可能なフィールドタイプの3つにすぎません。 完全なリストはフォームフィールドにあります。
ウィジェット
各フォームフィールドには、対応するウィジェットクラスがあり、これは<input type="text">
などのHTMLフォームウィジェットに対応します。
ほとんどの場合、フィールドには適切なデフォルトウィジェットがあります。 たとえば、デフォルトでは、 CharField には TextInput ウィジェットがあり、HTMLで<input type="text">
を生成します。 代わりに<textarea>
が必要な場合は、message
フィールドで行ったように、フォームフィールドを定義するときに適切なウィジェットを指定します。
フィールドデータ
フォームで送信されたデータが何であれ、is_valid()
を呼び出して正常に検証されると(is_valid()
がTrue
を返すと)、検証されたフォームデータは[X181X ] 辞書。 このデータは、Pythonタイプにうまく変換されています。
ノート
この時点でも、request.POST
から未検証のデータに直接アクセスできますが、検証済みのデータの方が優れています。
上記の連絡フォームの例では、cc_myself
はブール値になります。 同様に、 IntegerField や FloatField などのフィールドは、値をそれぞれPython int
とfloat
に変換します。
このフォームを処理するビューでフォームデータを処理する方法は次のとおりです。
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['[email protected]']
if cc_myself:
recipients.append(sender)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
一部のフィールドタイプでは、追加の処理が必要です。 たとえば、フォームを使用してアップロードされたファイルは、別の方法で処理する必要があります(request.POST
ではなく、request.FILES
から取得できます)。 フォームでファイルのアップロードを処理する方法の詳細については、アップロードされたファイルをフォームにバインドするを参照してください。
フォームテンプレートの操作
フォームをテンプレートに取り込むために必要なのは、フォームインスタンスをテンプレートコンテキストに配置することだけです。 したがって、フォームがコンテキストでform
と呼ばれる場合、テンプレート:Form
はその<label>
および<input>
要素を適切にレンダリングします。
フォームレンダリングオプション
追加のフォームテンプレート家具
フォームの出力には、周囲の<form>
タグまたはフォームのsubmit
コントロールが含まれていないことを忘れないでください。 これらは自分で用意する必要があります。
<label>
/ <input>
ペアには、他にも出力オプションがあります。
テンプレート:Form.as table
は、<tr>
タグでラップされたテーブルセルとしてそれらをレンダリングしますテンプレート:Form.as p
は、それらを<p>
タグでラップしてレンダリングしますテンプレート:Form.as ul
は、それらを<li>
タグでラップしてレンダリングします
周囲の<table>
または<ul>
要素を自分で指定する必要があることに注意してください。
ContactForm
インスタンスのテンプレート:Form.as p
の出力は次のとおりです。
<p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label>
<textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
<input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
各フォームフィールドには、id_<field-name>
に設定されたID属性があり、付随するラベルタグによって参照されることに注意してください。 これは、スクリーンリーダーソフトウェアなどの支援技術がフォームにアクセスできるようにするために重要です。 ラベルとIDの生成方法をカスタマイズすることもできます。
詳細については、フォームをHTMLとして出力するを参照してください。
フィールドを手動でレンダリングする
Djangoにフォームのフィールドを解凍させる必要はありません。 必要に応じて手動で行うことができます(たとえば、フィールドを並べ替えることができます)。 各フィールドは、テンプレート:Form.name of field
を使用してフォームの属性として使用でき、Djangoテンプレートでは適切にレンダリングされます。 例えば:
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
完全な<label>
要素は、 label_tag()を使用して生成することもできます。 例えば:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
フォームのエラーメッセージのレンダリング
この柔軟性の代償はもう少し手間がかかります。 これまで、フォームエラーの表示方法について心配する必要はありませんでした。 この例では、各フィールドのエラーとフォーム全体のエラーを確実に処理する必要がありました。 フォームの上部にあるテンプレート:Form.non field errors
と、各フィールドのエラーのテンプレートルックアップに注意してください。
テンプレート:Form.name of field.errors
を使用すると、フォームエラーのリストが表示され、順序付けられていないリストとして表示されます。 これは次のようになります。
<ul class="errorlist">
<li>Sender is required.</li>
</ul>
リストにはerrorlist
のCSSクラスがあり、外観のスタイルを設定できます。 エラーの表示をさらにカスタマイズしたい場合は、エラーをループすることでカスタマイズできます。
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
非フィールドエラー(および/またはform.as_p()
などのヘルパーを使用するときにフォームの上部に表示される非表示フィールドエラー)は、nonfield
の追加クラスでレンダリングされ、それらを区別しやすくなります。フィールド固有のエラー。 たとえば、テンプレート:Form.non field errors
は次のようになります。
<ul class="errorlist nonfield">
<li>Generic validation error</li>
</ul>
エラー、スタイル設定、およびテンプレートでのフォーム属性の操作の詳細については、 Forms API を参照してください。
フォームのフィールドをループする
各フォームフィールドに同じHTMLを使用している場合は、{% for %}
ループを使用して各フィールドを順番にループすることで、重複するコードを減らすことができます。
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
テンプレート:Field
の便利な属性は次のとおりです。
テンプレート:Field.label
フィールドのラベル、例:
Email address
。テンプレート:Field.label tag
適切なHTML
<label>
タグでラップされたフィールドのラベル。 これには、フォームの label_suffix が含まれます。 たとえば、デフォルトのlabel_suffix
はコロンです。<label for="id_email">Email address:</label>
テンプレート:Field.id for label
このフィールドに使用されるID(上記の例では
id_email
)。 手動でラベルを作成する場合は、label_tag
の代わりにこれを使用することをお勧めします。 たとえば、インラインJavaScriptがあり、フィールドのIDをハードコーディングしたくない場合にも役立ちます。テンプレート:Field.value
フィールドの値。 例:
[email protected]
。テンプレート:Field.html name
入力要素の名前フィールドで使用されるフィールドの名前。 これは、フォームプレフィックスが設定されている場合、それを考慮に入れます。
テンプレート:Field.help text
フィールドに関連付けられているヘルプテキスト。
テンプレート:Field.errors
このフィールドに対応する検証エラーを含む
<ul class="errorlist">
を出力します。{% for error in field.errors %}
ループを使用して、エラーの表示をカスタマイズできます。 この場合、ループ内の各オブジェクトは、エラーメッセージを含む文字列です。テンプレート:Field.is hidden
この属性は、フォームフィールドが非表示フィールドの場合は
True
であり、それ以外の場合はFalse
です。 テンプレート変数としては特に有用ではありませんが、次のような条件付きテストでは役立つ可能性があります。
{% if field.is_hidden %}
{# Do something special #}
{% endif %}
テンプレート:Field.field
- この BoundField がラップするフォームクラスの Field インスタンス。 これを使用して、フィールド属性にアクセスできます。
テンプレート:Char field.field.max length
。
再利用可能なフォームテンプレート
サイトで複数の場所のフォームに同じレンダリングロジックを使用している場合は、フォームのループをスタンドアロンテンプレートに保存し、:ttag: `include` タグを使用して他のテンプレートで再利用することにより、重複を減らすことができます。
# In your form template:
{% include "form_snippet.html" %}
# In form_snippet.html:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
テンプレートに渡されたフォームオブジェクトの名前がコンテキスト内で異なる場合は、:ttag: `include` タグのwith
引数を使用してエイリアスを作成できます。
{% include "form_snippet.html" with form=comment_form %}
これを頻繁に行う場合は、カスタム包含タグの作成を検討してください。