先日の Django Meet up で読んだ Tow Scoops of Django 1.6 に載っていた django-model-utils がいい感じだったので、ちょっと調べてみました。
このエントリーでは、以下の環境で動作確認しています。
- Python 3.4
- django-model-utils 2.0.3
- Django 1.6.2
あんまりDjangoの詳しい使い方は解説はしません。 Djangoのチュートリアル ぐらいは読んでいる前提で書きます。
インストール
インストールは ドキュメント の方法でもいいですが、手っ取り早く使ってみたいなら、予めdjango-model-utilsが組み込まれたテンプレート django-twoscoops-project が便利です。
ただし、django-twoscoops-projectに組み込まれているdjango-model-utilsのバージョンは少し古いので、このままだと最新版の一部機能が使えません。
django-twoscoops-projectを使う場合は、以下の手順でdjango-model-utilsのバージョンを変更してください。
StatusField
まず、こんなmodelを作ります:
from django.db import models
class Article(models.Model):
STATUSES = (
(0, 'draft'),
(1, 'published'),
)
status = models.IntegerField(choices=STATUSES)
ステータスでデータの状態を表す、よくある設計です。
この設計でArticleモデルを操作するコードを書くと:
>>> article = Article.objects.create(status=1)
または:
>>> article = Article.objects.create(status=Article.STATUSES[1][0])
となります。どちらもマジックナンバーになっていて分かりにくいコードですね。
マジックナンバーにならないように書くなら:
from django.db import models
class Article(models.Model):
DRAFT, PUBLISHED = 0, 1
STATUSES = (
(DRAFT, 'draft'),
(PUBLISHED, 'published'),
)
status = models.IntegerField(choices=STATUSES)
という手がありますが、ちょっと長ったらしいです。
django-model-utilsの StatusField を使うと、これをスッキリ書けます:
from django.db import models
from model_utils.fields import StatusField
from model_utils import Choices
class Article(models.Model):
STATUS = Choices('draft', 'published')
status = StatusField()
Articleモデルを操作するコードはこう書きます:
>>> Article.objects.create(status=Article.STATUS.draft)
Monitorfield
さらに、 MonitorField で「ステータスが更新された日時を記録する」フィールドを作れます:
from django.db import models
from model_utils.fields import StatusField, MonitorField
from model_utils import Choices
class Article(models.Model):
STATUS = Choices('draft', 'published')
status = StatusField()
status_changed = MonitorField(monitor='status')
これだけです。saveメソッドのオーバーライドなんて必要ありません!:
>>> article = Article.objects.create(status=Article.STATUS.draft)
>>> article.status_changed
datetime.datetime(2014, 4, 7, 15, 30, 28, 513992, tzinfo=<UTC>)
>>> article.status = Article.STATUS.published
>>> article.save()
>>> article.status_changed # 日時が記録されている
datetime.datetime(2014, 4, 7, 15, 31, 54, 869299, tzinfo=<UTC>)
ステータスが特定の値のときのみ日時を記録したい、ということなら:
from django.db import models
from model_utils.fields import StatusField, MonitorField
from model_utils import Choices
class Article(models.Model):
STATUS = Choices('draft', 'published')
status = StatusField()
status_changed = MonitorField(monitor='status', when=['published'])
これで、ステータスをpublishedにした時のみ日時が記録されます:
>>> article = Article.objects.create(status=Article.STATUS.draft)
>>> article.status_changed
datetime.datetime(2014, 4, 7, 15, 44, 24, 933748, tzinfo=<UTC>)
>>> article.status = Article.STATUS.draft
>>> article.save()
>>> article.status_changed # statusがpublishedではないので、日時が記録されない
datetime.datetime(2014, 4, 7, 15, 44, 24, 933748, tzinfo=<UTC>)
>>> article.status = Article.STATUS.published
>>> article.save()
>>> article.status_changed # statusがpublishedに変更されたので、日時が記録されている
datetime.datetime(2014, 4, 7, 15, 46, 20, 313597, tzinfo=<UTC>)
SplitField
最後は SplitField です。
SplitFieldは、ブログ記事のように「先頭の文章を少しだけ表示させて、続きは別のページにリンクを貼って誘導する」といった仕様のアプリケーションを作るときに役立ちます。
まず、このコードを見てください:
from django.db import models
from model_utils.fields import SplitField
from model_utils import Choices
class Article(models.Model):
title = models.CharField(max_length=100)
body = SplitField()
bodyは、マーカー「<!– split –>」を境に先頭の文章と続きの文章に分けることができます1 :
>>> article = Article.objects.create(title='hello!', body='先頭の文章ここまでがexcerptに出力されます\n\n<!-- split -->\n\nここから先はcontentに出力されます')
>>> article.body.excerpt
'先頭の文章ここまでがexcerptに出力されます\n'
>>> article.body.content
'先頭の文章ここまでがexcerptに出力されます\n\n<!-- split -->\n\nここから先はcontentに出力されます'
マーカーが含まれない文字列の場合は、最初の2つの段落[^2][^3]
をexcerptと判定します:
>>> article = Article.objects.create(title='hello!', body='1行目\n\n2行目\n\n3行目\n4行目')
>>> article.body.excerpt
'1行目\n\n2行目'
>>> article.body.content
'1行目\n\n2行目\n\n3行目\n4行目'
Models編に続く!(かもしれない)
マーカーになる値は設定ファイルのSPLIT_MARKERで変更できます。 ↩︎