ブックマークアプリを作ってみるシリーズ。
前回まででモデルの定義はとりあえずOKとしますが、もう少し、特に管理画面を使いやすくするための設定を加えていくことにします。これを実現するために以下の3点をmodels.pyへ加えていきます。
- def __str__():
- class Admin:
- class Meta:
__str__()
増田さんのドキュメントによれば:
__str__() は,オブジェクトに対して str() を呼び出した際に返す内容を定義するための Python の特殊メソッドです. Django はそこかしこで
str(obj) を使っています.
そこらじゅうで使われてるらしいのでやっぱり設定しておくべきでしょう。インスタンスのタイトルなどを表示させるのがよさそうなので、ブックマークモデル、タグモデルの両方で以下のようにしておきました。
タグモデル:
class Tag(models.Model):
title = models.CharField(maxlength = 20)
(中略)
def __str__(self):
return self.title # titleアトリビュートで設定したものを返す
ブックマークモデル:
class Bookmark(models.Model):
title = models.CharField(maxlength = 30)
(中略)
def __str__(self):
return self.title # titleアトリビュートで設定したものを返す
もちろん他のものを返してもOKです。例えばブックマークではURLを返したいというような場合は:
class Bookmark(models.Model):
url = models.URLField(unique = True)
(中略)
def __str__(self):
return self.url # urlアトリビュートで設定したものを返す
とすればよいですが、長すぎるURLとかだと非常にうざくなりそうなので、タイトル・名前くらいがちょうどいいんじゃないでしょうか。
Meta
Metaクラスにはいくつかの設定項目がありますが、管理画面を使いやすくするのに便利な項目には例えば以下のようなものがあります。
- verbose_name
- オブジェクトの別名を設定する。例えばBookmarkに対して「'ブックマーク'」などとする。文字列で表現する
- verbose_name_plural
- オブジェクトの別名の複数形。verbose_nameと同じでもさほど問題なさげ。文字列で表現する
- ordering
- オブジェクトのリストをどのフィールドを元にして並べるのかを設定する 。文字列のリストで表現する
orderingにはフィールド名を指定します(指定しない場合はPrimary Key順、つまり追加した順に並びます)。フィールド名をそのまま記述すれば昇順に、マイナス「-」をつけると降順に並びます。例えば現在ブックマークモデルには以下のようなフィールドが定義されています:
class Bookmark(models.Model):
title = models.CharField(maxlength = 30)
url = models.URLField(unique = True)
summary = models.TextField(maxlength = 200, blank = True)
tags = models.ManyToManyField(Tag)
date = models.DateField(auto_now = True)
この場合は、title, url, summary, tags, dateで並び替えることができます。タイトル順にしたい場合は
class Meta:
ordering = ['title']
としてやります。dateで並ばせたい場合にはもちろん、
class Meta:
ordering = ['date']
dateで降順にしたい場合はマイナス(というかハイフン)を付けます。
class Meta:
ordering = ['-date']
*複数指定することもできますが、管理画面では最初のフィールドのみ有効になるようです。
ということで以下のような感じで設定しました。
タグモデルのMeta:
class Tag(models.Model):
title = models.CharField(maxlength = 20)
(中略)
class Meta:
ordering = ['title'] # titleで並べる
verbose_name = 'tags' # オブジェクトの別名
verbose_name_plural = 'tag items' # オブジェクトの別名の複数形
ブックマークモデルのMeta:
class Bookmark(models.Model):
title = models.CharField(maxlength = 30)
(中略)
date = models.DateField(auto_now = True)
class Meta:
ordering = ['-date'] # dateで降順に並べる
verbose_name = 'bookmarks' # オブジェクトの別名
verbose_name_plural = 'bookmark items' # オブジェクトの別名の複数形
Admin
Adminはモデルを管理画面に現すために必要です。とりあえずモデルの中にclass Admin:を書かなくてはなりません。で、モデルをどのように見せるのかをAdminクラスのアトリビュートとして表現していきます。設定項目はやはり増田さんのドキュメントを参照しましょう。
ここでは以下のようなアトリビュートを設定することにします。
- list_display
- 管理画面に表示するフィールドを指定する。文字列のタプルとして表現する
- date_hierarchy
- 日付で絞り込められるナビゲーションを追加するための項目。モデルの中にDateフィールドやDateTimeフィールドがある場合に使える
- list_filter
- 項目を絞り込められるサイドナビゲーションを追加するための項目。何で絞り込むかを文字列のタプルとして表現する
- search_fields
- 管理画面に検索ボックスを追加するための項目。検索対象となるフィールドを文字列のリストとして表現する
最初の「list_display」は管理画面でオブジェクトのリストを表示するときにどのフィールドを表示するのかを設定します。設定しないと__str__で設定したもののみ表示されてしまい、なんだか素っ気ない感じになってしまうので、例えばブックマークモデルのオブジェクトに関してはtitle以外にもurlとかtagとかを表示してやった方がいいかな、と。
その他の3つはオブジェクトを管理するにあたって絞り込みや検索など、使い勝手の良さを向上させるためのものですが、まぁ、無くてもよさげな雰囲気です。
ということでタグモデルのAdmin:
class Tag(models.Model):
title = models.CharField(maxlength = 20)
summary = models.CharField(maxlength = 100, blank = True)
class Admin:
# 管理画面でtitleとsummaryを表示
list_display = ('title', 'summary')
ブックマークモデルのAdmin:
class Bookmark(models.Model):
title = models.CharField(maxlength = 30)
url = models.URLField(unique = True)
summary = models.TextField(maxlength = 200, blank = True)
tags = models.ManyToManyField(Tag)
date = models.DateField(auto_now = True)
class Admin:
# 管理画面でtitleとurlとtagsを表示
# ホントは問題アリ(詳しくは後述)
list_display = ('title', 'url', 'tags')
# tagsとdateで絞り込みできるようにする
list_filter = ('tags', 'date')
# dateで絞り込みできるナビゲーションを追加
date_hierarchy = 'date'
# タイトルを対象として検索できるサーチボックスを追加
search_fields = ['title']
ただしこのままではブックマークモデルの「list_display」に少し問題があります。ドキュメントに特殊なケースとして書かれていますが、
list_display にはいくつか特殊なケースがあります:
- ManyToManyField フィールドの表示は,テーブルの各行に対して個別に
SQL 文を実行することになってしまうのでサポートしていません.どうしても表示させたいなら,カスタムメソッドをモデルに実装して,メソッドの名前を list_display に追加してください (list_display へのカスタムメソッドの追加については,後で詳しく説明しています).
ブックマークモデルでは「tags」は「ManyToManyField」なので以下のようではうまくいきません。
list_display = ('title', 'url', 'tags')「'tags'」の代わりに、カスタムメソッドをモデルに実装してそのメソッド名をここに追加してやる必要があります。
カスタムメソッドを作る
あるブックマークに対して付与されているタグの一覧を得るメソッドを書いてやりましょう。データベースAPIの中にall()メソッドがありますので、これによってオブジェクトの一覧を取り出し、そのタイトルをリストとして表示してやるようなメソッドを書きます。
def selected_tags(self):
'''return selected tags'''
return [x.title for x in self.tags.all()]
リスト内包表記を使わずに表現するならこんな感じでしょうか?:
def selected_tags(self):
'''return selected tags'''
tag_titles = []
for tag_obj in self.tags.all():
tag_titles.append(tag_obj.title)
return tag_titles
いずれにしろこのselected_tagsというメソッドは、ブックマークに付与されたタグのタイトルをリストで返すようになります。ということで、
list_display = ('title', 'url', 'selected_tags')
これでいいでしょう。
まとめ
前回のモデルの定義に管理画面用のものを付け足して、以下のようになりました。
# vim: fileencoding = utf-8
from django.db import models
class Tag(models.Model):
title = models.CharField(maxlength = 20)
summary = models.CharField(maxlength = 100, blank = True)
def __str__(self):
return self.title
class Admin:
list_display = ('title', 'summary')
class Meta:
ordering = ['title']
verbose_name = 'tags'
verbose_name_plural = 'tag items'
class Bookmark(models.Model):
title = models.CharField(maxlength = 30)
url = models.URLField(unique = True)
summary = models.TextField(maxlength = 200, blank = True)
tags = models.ManyToManyField(Tag, filter_interface = models.HORIZONTAL)
date = models.DateField(auto_now = True)
def __str__(self):
return self.title
def selected_tags(self):
'''return selected tags'''
tag_titles = []
for tag_obj in self.tags.all():
tag_titles.append(tag_obj.title)
return tag_titles
class Admin:
list_display = ('title', 'url', 'selected_tags')
list_filter = ('tags', 'date')
date_hierarchy = 'date'
search_fields = ['title']
class Meta:
ordering = ['-date']
verbose_name = 'bookmarks'
verbose_name_plural = 'bookmark items'
*文中では触れませんでしたが、ブックマークのtagsアトリビュートに「filter_interface = models.HORIZONTAL」を追加しています。
ここでプロジェクトディレクトリにて「manage.py syncdb」すれば、これらのモデルがデータベーステーブルに反映されます。