nullpobug勉強会03 "ContentTypesを使おう"
>03
たぶん。
去る土曜に開催されました月イチnullpobug勉強会。今回のテーマはContentTypes。
- メンバー
講師:id:nullpobug
生徒:id:feiz
ContentTypesってなんだ
インスタンスとモデル名とを結びつける仕組み。とでも言えばいいんだろうか・・・
Djangoで以下のようにModelを継承してみる。
class Update(models.Model): name = models.CharField(max_length=64) date = models.DateTimeField() class Todo(Update): task_name = models.CharField(max_length=64) target_date = models.DateField() class Entry(Update): title = models.CharField(max_length=64) body = models.TextField()
で、Updateモデルからデータを引くと、TodoのインスタンスとEntryのインスタンスが混ざったリストが引けちゃいます。
Update.dateで降順ソートして最新n件を表示するような汎用ビューと、日付と名前と更新内容を表示するテンプレートを書けばサイト更新履歴のようなものがすぱっと実装できます。素晴らしい。
がしかし、テンプレート上でforで回して表示しようとしてみると頭を抱えてしまう。
あくまでUpdateモデルから引いてるので見た目上はこれらはUpdateのインスタンスです。
しかしそれがTodoなのかEntryなのか分からないと何をどう表示すればいいのか分かりません。
このような「このインスタンスはどのモデルのインスタンスなのか」を判別して処理を分ける必要がある時にContentTypesがとても便利です。
中身
ContentTypesのメインはContentTypeモデルです。
ContentTypeモデルの中にはプロジェクトにインストールされている全てのモデルの情報が記録されています。*1
これにリレーションを張る事でインスタンスとモデル名とが結びつけられます。
適用してみる
UpdateモデルにContentTypeのフィールドを持たせます。
from django.contrib.contenttypes.models import ContentType class Update(models.Model): title = models.CharField(max_length=64) date = models.DateTimeField() content_type = models.ForeignKey(ContentType)
リレーションが張れました。
ちなみにデフォルトのDjangoプロジェクトだとContentTypesは最初からインストールされてます。もしデフォルトから変更してる場合はインストールしておいて下さい。
あとはSaveをオーバーライドして自動でcontent_typeに値が入るように。
#Updateモデルに追加 def save(self): self.content_type = ContentType.objects.get_for_model(self.__class__) super(Update, self).save()
get_for_modelはクラスかクラスのインスタンスを引数にしてそれらを表すContentTypeインスタンスを返してくれます。
これでUpdate.objects.all()などとしたときもcontent_typeの値で元の型が判別出来ます。
あとは表示ですが
#Updateに追加 def display(self): obj = getattr(self, self.content_type.name) return render_to_string("myapp/_" + self.content_type.name + ".html", {"object": obj})
displayメソッドなんてのを親クラスに持たせて、テンプレートではupdate.displayを表示するようにすればらくちん。
displayの返り値の内容は"_" + "モデル名" + ".html"って名前のテンプレートでそのインスタンスをレンダリングした内容。
こうしておけば新しいモデルを追加したいときの作業が
- Updateを継承してモデルを作る
- テンプレートフォルダに「_モデル名.html」というテンプレートを追加
の2つだけになるので更にらくちんです。
まとめ
ContentTypesを使うと「いろんな型が混ざったモデル」が簡単に実現できます。
更新履歴以外にも使えるところはいくらでもありますので、是非とも利用してみて下さい。