前置き
この記事のつづきです。
「プロジェクト的単位」というのはタスク管理ツールなどで言う "プロジェクト" の事で、ここでは演劇の管理をするので「公演」がそれに当たります。
- -> GitHub のリポジトリ (Python 3.8.1, Django 3.0.3)
記事の内容
前回は、「公演」に属するデータの入力インターフェイスについて考えました。
今回は、自分以外のユーザを招待して、「公演」のメンバに加える仕組みについて考えてみたいと思います。
- 使ったテクニックについては「まとめ」にまとめてあります。
Heroku に動くデモがあります。詳しくは GitHub Pages を御覧ください。
具体的な目標
- 「公演」の所有者が、特定のアカウントに向けて「招待状」というものを作れるようにします。
- 作った「招待状」を確認したり削除したり出来るようにします。
- 招待されたアカウントでログインすると「招待状」が表示されるようにします。
- 受け取った「招待状」から「公演」に参加したり、受け取った「招待状」を削除したり出来るようにします。
- メール等は使わず、Web アプリ内で完結する仕組みにします。
完成イメージ
やりたい事の整理
この Web アプリでは "公演ユーザ" というモデルを使って、Django が管理しているアカウントと各「公演」への参加状態を関連付けています。
したがって、招待された人が「参加」を選択した時の処理は、"公演ユーザ" を新規で作成する処理になります。
「招待状」のモデルを作る
以下のフィールドを持たせます。
フィールド | 内容 |
---|---|
production | 招待先の「公演」 |
inviter | 招待する人 (Django が管理しているアカウント) |
invitee | 招待される人 (Django が管理しているアカウント) |
exp_dt | 招待状の有効期限 |
「招待状」を作成する仕組みを作る
- 「メンバ一覧」という画面があるので、そこに「招待する」というボタンを作ります。
- このボタンを押すと「招待状」を作る画面を表示するようにします。
- 「招待状」を作る画面では、「招待される人」を指定できるようにする必要があります (それ以外のフィールドの内容はアプリ側で決められます)。
「招待状」を表示する仕組みを作る
- 「公演」の所有者が「メンバ一覧」画面を表示した時に、自分が作った「招待状」が表示されるようにします。
- ここで削除もできるようにします。
- 招待されている人が「参加している公演」画面 (ログイン直後に表示される画面) を表示した時に、自分宛ての「招待状」が表示されるようにします。
- ただし、有効期限を過ぎているものは表示されないようにします。
- ここで「公演」への参加と、「招待状」の削除ができるようにします。
「招待状」から「公演」に参加する仕組みを作る
- 招待された人が「参加」を選択した時の処理は、"公演ユーザ" を新規で作成する処理になります。
招待状のモデル
※コードでは「招待状」ではなく「招待」と呼んでいます。
※「公演」に参加しているメンバをまとめて「座組」と呼んでいます。
from datetime import datetime, timezone
from django.conf import settings
from django.db import models
class Invitation(models.Model):
'''座組への招待
'''
production = models.ForeignKey(Production, verbose_name='公演',
on_delete=models.CASCADE)
inviter = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='招待する人',
related_name='inviter', on_delete=models.CASCADE)
invitee = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='招待される人',
related_name='invitee', on_delete=models.CASCADE)
exp_dt = models.DateTimeField(verbose_name='期限')
class Meta:
verbose_name = verbose_name_plural = '座組への招待'
ordering = ['exp_dt']
def __str__(self):
return f'{self.invitee} さんへの {self.production} への招待'
def expired(self):
'''この招待が期限切れかどうかを返す
'''
now = datetime.now(timezone.utc)
return now > self.exp_dt
-
Production
は、この Web アプリで定義している「公演」のモデルです。 -
inviter
,invitee
は、どちらも Django が管理しているアカウントなので、settings.AUTH_USER_MODEL
のオブジェクトへの参照となります。 -
AUTH_USER_MODEL
についてはこちらを御覧ください。
招待状を作成する仕組み
以下、View や Template の説明になります。
production
というアプリケーションの中で作っているので、URL の指定が 'production:usr_list' のようになっています。
呼び出しのための urls.py
の書き方については、production/urls.py
のソース を見てください。
View を作る
CreateView
を継承して 招待状を作成するための View を作ります。
from django.views.generic.edit import CreateView
from django.contrib import messages
from django.contrib.auth import get_user_model
from .view_func import *
from .models import Production, ProdUser, Invitation
class InvtCreate(LoginRequiredMixin, CreateView):
'''Invitation の追加ビュー
'''
model = Invitation
fields = ()
-
fields
を空にしているのは、ちょっとトリッキーかも知れませんが、フィールド (id
以外) の値をすべてForm
ではなくコードでセットするためです。
def get(self, request, *args, **kwargs):
'''表示時のリクエストを受けるハンドラ
'''
# 所有権を検査してアクセス中の公演ユーザを取得する
prod_user = test_owner_permission(self)
# production を view の属性として持っておく
# テンプレートで固定要素として表示するため
self.production = prod_user.production
return super().get(request, *args, **kwargs)
- 公演の所有権を持つユーザだけにアクセスを許可するため、
test_owner_permission()
というメソッドを呼んでいます。- このメソッドが何をしているかについては、
production/view_func.py
のソース を見てください。
- このメソッドが何をしているかについては、
def post(self, request, *args, **kwargs):
'''保存時のリクエストを受けるハンドラ
'''
# 所有権を検査してアクセス中の公演ユーザを取得する
prod_user = test_owner_permission(self)
# フォームで入力された「招待する人の ID」
invitee_value = request.POST['invitee_id']
# それに一致するユーザを view の属性として持っておく
user_model = get_user_model()
invitees = user_model.objects.filter(username=invitee_value)
if len(invitees) > 0:
self.invitee = invitees[0]
# prod_user, production を view の属性として持っておく
# バリデーションと保存時に使うため
self.prod_user = prod_user
self.production = prod_user.production
return super().post(request, *args, **kwargs)
- 招待状を作成する画面では UI の文脈上、「招待される人」の事を「招待する人」と書いています。
- 「招待される人」は
ForeignKey
型なので、通常はForm
で選択式の UI を表示するのですが、すべてのユーザを選択肢に出すのもどうかと思うので、テキストでアカウント ID (実際のフィールド名はusername
) を入力する形にしました。 - 入力された ID を元にユーザのオブジェクトを見つける処理は
post()
メソッドでやっています。-
User
モデルを参照するにはget_user_model()
を使います (> ドキュメント)。
settings.AUTH_USER_MODEL
は文字列だからです。
-
- 見つかった時だけ View の
invitee
属性をセットするようにして、バリデーションの時にこれの存在チェックをするようにしています。
def form_valid(self, form):
'''バリデーションを通った時
'''
# POST ハンドラで招待するユーザを取得できていなかったら、追加失敗
if not hasattr(self, 'invitee'):
return self.form_invalid(form)
# 公演ユーザのユーザ ID リスト
prod_users = ProdUser.objects.filter(production=self.production)
prod_user_user_ids = [prod_user.user.id for prod_user in prod_users]
# 招待中のユーザの ID リスト
invitations = Invitation.objects.filter(production=self.production)
current_invitee_ids = [invt.invitee.id for invt in invitations]
# 公演ユーザや招待中のユーザを招待することは出来ない。
if self.invitee.id in prod_user_user_ids\
or self.invitee.id in current_invitee_ids:
return self.form_invalid(form)
# 追加しようとするレコードの各フィールドをセット
instance = form.save(commit=False)
instance.production = self.production
instance.inviter = self.prod_user.user
instance.invitee = self.invitee
# 期限は7日
# デフォルトで UTC で保存されるが念の為 UTC を指定
instance.exp_dt = datetime.now(timezone.utc) + timedelta(days=7)
messages.success(self.request, str(instance.invitee) + " さんを招待しました。")
return super().form_valid(form)
-
fields
が空なので (Form
がバリデートするものが無いので)、ユーザが招待状を作ろうとすると必ずこのメソッドを通ります。 - 指定した ID のユーザの存在、そのユーザがすでに参加していないか、招待されていないかをチェックします。
- すべてのフィールドをセットしてスーパーメソッドを呼べば、作成した招待状が保存されます。
def get_success_url(self):
'''追加に成功した時の遷移先を動的に与える
'''
prod_id = self.prod_user.production.id
url = reverse_lazy('production:usr_list', kwargs={'prod_id': prod_id})
return url
def form_invalid(self, form):
'''追加に失敗した時
'''
messages.warning(self.request, "招待できませんでした。")
return super().form_invalid(form)
-
get_success_url()
と、失敗した時のform_invalid()
です。 - 戻り先 (
production:user_list
) は、「招待する」ボタンを設置しようとしている View (「メンバ一覧」画面) です。
Template を作る
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
<a href="{% url 'production:usr_list' prod_id=view.production.id %}">◀</a>
座組への招待
</h1>
<div> </div>
<form method="post">
{% csrf_token %}
<table>
<tr><th>公演</th><td>{{ view.production }}</td></tr>
<tr><th>招待する人の ID</th><td><input type="text" name="invitee_id" /></td></tr>
</table>
<input type="submit" value="招待">
</form>
{% endblock %}
-
invitee_id
を入力させて「招待」ボタンで submit しているだけです。
作った招待状を表示する仕組み
自分が作った招待状を表示する画面は、「メンバ一覧」画面です。ここに「招待する」ボタンも設置します。
ユーザシナリオは以下のようになります。
- 公演ごとの「メンバ一覧」画面で「招待する」を押す。
- 招待状を作成するための (上記で作った) View が表示される。
- 招待する (される) ユーザの ID を入力してフォームを submit する。
- 「メンバ一覧」画面に戻って、自分が作った招待状を確認できる。
View を作る
from django.views.generic import ListView
from django.core.exceptions import PermissionDenied
class UsrList(LoginRequiredMixin, ListView):
'''ProdUser のリストビュー
'''
model = ProdUser
def get(self, request, *args, **kwargs):
'''表示時のリクエストを受けるハンドラ
'''
# アクセス情報から公演ユーザを取得しアクセス権を検査する
prod_user = accessing_prod_user(self, kwargs['prod_id'])
if not prod_user:
raise PermissionDenied
# テンプレートから参照できるよう、ビューの属性にしておく
self.prod_user = prod_user
# 招待中のユーザを表示するため、ビューの属性にする
self.invitations = Invitation.objects.filter(production=prod_user.production)
return super().get(request, *args, **kwargs)
# 以下略
- 本来は「公演ユーザ」の一覧を表示する View ですが、同じ画面に招待状の一覧も表示できるよう、招待状を View の属性 (
invitations
) にして Template から参照できるようにしています。 - 前の節で「自分が作った招待状」と書きましたが、厳密には「この公演に招待している招待状」を取得しています。理由は、「公演」の所有者が変わっても表示されるようにするためです。
Template を作る
招待状を表示している部分だけ抜粋します。
全体を見るには GitHub 上のコードにアクセスして下さい。
{% if view.prod_user.is_owner and view.invitations %}
<div style="border:solid thin lightgray; border-radius:10px; padding: 10px; margin:5px 0 20px;">
<p><strong>招待中のユーザ</strong></p>
<table>
<tr>
<th>ユーザ ID</th>
<th>姓</th>
<th>名</th>
<th>招待の期限</th>
</tr>
{% for item in view.invitations %}
<tr>
<td>{{ item.invitee.username }}</td>
<td>{{ item.invitee.last_name }}</td>
<td>{{ item.invitee.first_name }}</td>
{% if item.expired %}
<td style="color:red;">{{ item.exp_dt }}</td>
{% else %}
<td>{{ item.exp_dt }}</td>
{% endif %}
<td><a href="{% url 'production:invt_delete' pk=item.id from='usr_list' %}" class="deletelink">削除</a></td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
- 最初の
if
文で、アクセス中のユーザが公演の所有者である場合、かつ View にinvitations
属性が存在する (そして空でない) 場合という条件で、招待状の一覧を表示するようにしています。 - 招待の期限が切れている場合は赤く表示したいので、上で作った
Invitation
クラスのexpired
メソッドを使っています。- Template からメソッドを呼べることがポイントです。
- 各招待状には「削除」ボタンをつけて、これから作る削除用の View にジャンプするようにします。
- 招待された人も同じ View を使って削除できるようにするので、URL に
from
というパラメタをつけて戻り先を憶えるようにしています。
- 招待された人も同じ View を使って削除できるようにするので、URL に
受け取った招待状を表示する仕組み
受け取った招待状を表示する画面は、「参加している公演」画面です。
本来は参加している公演の一覧を表示する画面ですが、作った招待状を表示する時と同じように、条件を満たした時だけ招待状のためのエリアを表示します。
View を作る
class ProdList(LoginRequiredMixin, ListView):
'''Production のリストビュー
ログインユーザの公演のみ表示するため、モデルは ProdUser
'''
model = ProdUser
template_name = 'production/production_list.html'
def get(self, request, *args, **kwargs):
'''表示時のリクエストを受けるハンドラ
'''
# 座組への招待を表示するため、ビューの属性にする
now = datetime.now(timezone.utc)
self.invitations = Invitation.objects.filter(invitee=self.request.user,
exp_dt__gt=now)
return super().get(request, *args, **kwargs)
# 以下略
- 作った招待状を表示する時と同じように、招待状を View の属性にしています。
- 作った招待状を表示する時との違いは、最初から期限が切れていない招待状だけを取り出している点です。
-
expired()
メソッドは使わず、QuerySet のfilter()
で抽出しています。 - ここでやっているような
xx__gt=xx
といった条件の書き方についてはこちらをご覧ください。
-
Template を作る
招待状を表示している部分だけ抜粋します。
全体を見るには GitHub 上のコードにアクセスして下さい。
{% if view.invitations %}
<div style="border:solid thin lightgray; border-radius:10px; padding: 10px; margin:5px 0 20px;">
<p><strong>招待されている座組</strong></p>
<table>
<tr>
<th>公演名</th>
<th>招待主の ID</th>
<th>招待の期限</th>
</tr>
{% for item in view.invitations %}
<tr>
<td>{{ item.production }}</td>
<td>{{ item.inviter.username }}</td>
{% if item.expired %}
<td style="color:red;">{{ item.exp_dt }}</td>
{% else %}
<td>{{ item.exp_dt }}</td>
{% endif %}
<td>
<a href="{% url 'production:prod_join' invt_id=item.id %}" class="addlink">参加</a>
<a href="{% url 'production:invt_delete' pk=item.id from='prod_list' %}" class="deletelink">削除</a>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
- 受け取った招待状には「削除」ボタンに加えて「参加」ボタンも表示します。
- 「参加」ボタンを押すと、参加の確認と処理をする View にジャンプするようにします。
招待状を削除する仕組み
作った招待状や受け取った招待状を表示する時、Template に「削除」ボタンを配置しました。
そのリンク (production:invt_delete
) からアクセスする View を作ります。
View を作る
from django.views.generic.edit import DeleteView
class InvtDelete(LoginRequiredMixin, DeleteView):
'''Invitation の削除ビュー
'''
model = Invitation
template_name_suffix = '_delete'
-
template_name_suffix
はデフォルトだと '_confirm_delete' となるようです。
このあたりはお好みで。
def get(self, request, *args, **kwargs):
'''表示時のリクエストを受けるハンドラ
'''
# 公演の所有者または招待の invitee であることを検査する
invt = self.get_object()
prod_id = invt.production.id
prod_user = accessing_prod_user(self, prod_id)
is_owner = prod_user and prod_user.is_owner
is_invitee = self.request.user == invt.invitee
if not (is_owner or is_invitee):
raise PermissionDenied
return super().get(request, *args, **kwargs)
- 削除しようとするオブジェクト (招待状) から「公演」の ID を取って、アクセス中のユーザがその公演の所有者か、または招待された人であることをチェックしています。
-
accessing_prod_user()
メソッドが何をしているかについては、production/view_func.py
のソース を見てください。
def post(self, request, *args, **kwargs):
'''保存時のリクエストを受けるハンドラ
'''
# 公演の所有者または招待の invitee であることを検査する
invt = self.get_object()
prod_id = invt.production.id
prod_user = accessing_prod_user(self, prod_id)
is_owner = prod_user and prod_user.is_owner
is_invitee = self.request.user == invt.invitee
if not (is_owner or is_invitee):
raise PermissionDenied
return super().post(request, *args, **kwargs)
-
POST
ハンドラもやっていることはGET
と同じです。
重複が気持ち悪ければメソッド化しても良いと思います。
def get_success_url(self):
'''削除に成功した時の遷移先を動的に与える
'''
if self.kwargs['from'] == 'usr_list':
prod_id = self.object.production.id
url = reverse_lazy('production:usr_list', kwargs={'prod_id': prod_id})
else:
url = reverse_lazy('production:prod_list')
return url
def delete(self, request, *args, **kwargs):
'''削除した時のメッセージ
'''
result = super().delete(request, *args, **kwargs)
messages.success(
self.request, str(self.object) + " を削除しました。")
return result
-
「削除」ボタンのリンク URL に
from
パラメタを付けたので、それを使って戻り先の振り分けをしています。 -
get()
やpost()
メソッド内でなくても、self.kwargs
でURLConf
にアクセスできます。 -
URLConf
の指定は、production/urls.py
で以下のようにしています (> ソース)。path('invt_delete/<int:pk>/<str:from>/', views.InvtDelete.as_view(), name='invt_delete'),
Template を作る
「削除する」ボタンと「キャンセル」ボタンだけのシンプルなページです。
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
{% if view.kwargs.from == 'usr_list' %}
<a href="{% url 'production:usr_list' prod_id=object.production.id %}">◀</a>
{% else %}
<a href="{% url 'production:prod_list' %}">◀</a>
{% endif %}
座組への招待の削除
</h1>
<form method="post">{% csrf_token %}
<p>この招待を削除しますか?</p>
<input type="submit" value="削除する">
<input type="button"
{% if view.kwargs.from == 'usr_list' %}
onclick="location.href='{% url 'production:usr_list' prod_id=object.production.id %}'"
{% else %}
onclick="location.href='{% url 'production:prod_list' %}'"
{% endif %}
value="キャンセル">
</form>
<table>
<tr><th>公演</th><td>{{ object.production }}</td></tr>
<tr><th>招待される人の ID</th><td>{{ object.invitee.username }}</td></tr>
<tr><th>姓</th><td>{{ object.invitee.last_name }}</td></tr>
<tr><th>名</th><td>{{ object.invitee.first_name }}</td></tr>
<tr><th>期限</th>
{% if object.expired %}
<td style="color:red;">{{ object.exp_dt }}</td>
{% else %}
<td>{{ object.exp_dt }}</td>
{% endif %}
</tr>
</table>
{% endblock %}
- 「招待される人の ID」などは、
from
が 'prod_list' の場合 (招待されている人が開いた場合) は隠すなどしても良いかも知れません。 - Template 内で
URLConf
にアクセスするにはview.kwargs.from
などとします ('from' がパラメタ名の場合)。
招待状から公演に参加する仕組み
View を作る
「参加」を選択した時の処理は、"公演ユーザ" を新規で作成する処理なので、以下のような CreateView
を作ります。
ただし Template の名前は分かりやすく 'production_join.html' としました。
from django.http import Http404
class ProdJoin(LoginRequiredMixin, CreateView):
'''公演に参加するビュー
'''
model = ProdUser
fields = ('production', 'user')
template_name = 'production/production_join.html'
success_url = reverse_lazy('production:prod_list')
-
fields
に指定したproduction
とuser
は、ユーザに入力してもらう訳ではないのですが、hidden にして Template に埋め込むことでForm
から受け取れるようにしています。 - もちろん「招待状を作成する仕組み」でやったように、コードでセットすることも出来ます。
def get(self, request, *args, **kwargs):
'''表示時のリクエストを受けるハンドラ
'''
# 招待されているか検査し、参加できる公演を取得
self.production = self.production_to_join()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
'''保存時のリクエストを受けるハンドラ
'''
# 招待されているか検査し、参加できる公演を取得
self.production = self.production_to_join()
return super().post(request, *args, **kwargs)
-
GET
およびPOST
ハンドラでは、URLConf
の情報を元に、どの公演に参加するのかを属性として取っておきます。 - 参加先の公演 (
Production
) のオブジェクトを取得するproduction_to_join()
メソッドは、以下のようにしてあります。
def production_to_join(self):
'''招待されているか検査し、参加できる公演を返す
'''
# 招待がなければ 404 エラーを投げる
invt_id = self.kwargs['invt_id'];
invts = Invitation.objects.filter(id=invt_id)
if len(invts) < 1:
raise Http404
invt = invts[0]
# 招待が期限切れなら 404 エラーを投げる
now = datetime.now(timezone.utc)
if now > invt.exp_dt:
raise Http404
# アクセス中のユーザが invitee でなければ PermissionDenied
user = self.request.user
if user != invt.invitee:
raise PermissionDenied
return invt.production
-
「受け取った招待状を表示する仕組み」のところで、「参加」ボタンのリンクに
invt_id
というパラメタを埋め込んであるので、これを使って招待状 (Invitation
) のオブジェクトを取得しています。 -
ここ (公演に参加するビュー) へ来るためのパスは、
urls.py
で以下のようにしています (> ソース)。path('prod_join/<int:invt_id>/', views.ProdJoin.as_view(), name='prod_join'),
-
期限切れについてはちゃんとメッセージを表示した方が良いですが、ここでは単に 404 を返しています。
def form_valid(self, form):
'''バリデーションを通った時
'''
# 招待を検査
invt_id = self.kwargs['invt_id'];
invts = Invitation.objects.filter(id=invt_id)
if len(invts) < 1:
return self.form_invalid(form)
invt = invts[0]
# 保存するレコードを取得する
new_prod_user = form.save(commit=False)
# 正しい公演がセットされているか
if new_prod_user.production != invt.production:
return self.form_invalid(form)
# 正しいユーザがセットされているか
if new_prod_user.user != invt.invitee:
return self.form_invalid(form)
# 招待を削除
invt.delete()
messages.success(self.request, str(invt.production) + " に参加しました。")
return super().form_valid(form)
def form_invalid(self, form):
'''参加に失敗した時
'''
messages.warning(self.request, "参加できませんでした。")
return super().form_invalid(form)
-
Form
から正しいフィールド値 (この場合はproduction
とuser
) を受け取った後に View のform_valid()
メソッドが呼ばれるので、改めて「招待状」の内容と突き合わせます。 - このようなチェックが必要な理由は、フィールド値が一旦
Invitation
オブジェクトを離れて HTML に晒されているからです。 - チェックを通過したらスーパーメソッドを呼べば "公演ユーザ" が作成されますが、その前に「招待状」を削除しておきます。
Template を作る
「参加」ボタンと「あとで」ボタンだけのシンプルなページです。
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
<a href="{% url 'production:prod_list' %}">◀</a>
公演に参加
</h1>
<div> </div>
<p>
{{ view.production }} に招待されています。参加しますか?
</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="production" value="{{ view.production.id }}">
<input type="hidden" name="user" value="{{ view.request.user.id }}">
<input type="submit" value="参加">
<input type="button"
onclick="location.href='{% url 'production:prod_list' %}'"
value="あとで">
</form>
{% endblock %}
-
POST
ハンドラ以降の処理に渡したいフィールド値を、hidden にして直に書き込んでいます。 -
ForeignKey
型のフィールド値を直に書く場合は、オブジェクトのid
を使います。
まとめ
「公演」という "プロジェクト的単位" (グループ) にユーザを招待したり、招待を受けたユーザがそこに参加したりする機能の実装を紹介しました。
使ったテクニックについて以下にまとめます。
- モデルのフィールドとして
User
型を指定する時はsettings.AUTH_USER_MODEL
を使い、View のメソッドでUser
モデルを参照するにはget_user_model()
を使いました。- 実はモデルのフィールドとして指定する時も
get_user_model()
は使えるそうです。
- 実はモデルのフィールドとして指定する時も
- オブジェクトを作成する View でフィールド値をすべて (
id
以外) コードでセットする場合でも、fields
を空にすればCreateView
が使えました。 - Template で選択式の UI を使いたくない場合は、オブジェクトを見つけるためのフィールド (
username
など) の値を入力するようにして、コード (post()
メソッド) で見つけるようにしました。 - Python ではオブジェクトの属性を好きな時に追加できるので、有効な値が見つかった時だけ View の属性として持っておいて、あとで属性の有無を調べて (
hasattr()
メソッドを使って) バリデートする事ができました。 - 表示したいデータを View の属性にしておけば、Tamplate 側で属性の有無 (あっても空かどうか) を調べて、必要に応じて表示する事ができました。
- Template からは、View のメソッドを呼んで返り値を使う事もできました。
- 同じ View に複数のページからジャンプして戻り先を動的に決めたい場合、その View への URL に
from
等といったパラメタを持たせれば可能でした。 -
get()
やpost()
メソッドで受け取ったkwargs
は、あとからでもself.kwargs
として参照可能でした。- View の属性なので Template からも
view.kwargs.パラメタ名
として参照可能でした。
- View の属性なので Template からも
-
GET
の時点で作ろうとしているオブジェクトのフィールド値が分かっている場合は、Template に hidden で書いてしまうという方法もありました。 -
ForeignKey
型のフィールド値を (hidden 等として) Template に直に書く場合は、そのオブジェクトのid
を使えば、Form
で正しく処理されました。