DjangoのORMのすごいところ

私がDjangoのORMを気に入っているのは、Pythonコードでスマートにクエリセットを作れるからです。
例を書いてみる。

blog/models.py

from django.db import models
import datetime

class Entry(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    create_date = models.DateTimeField(default=datetime.datetime.now)

    def __unicode__(self):
        return self.title

こんなモデルを作った。

Qオブジェクトを使ってクエリセットを作ってみる

In [1]: from blog.models import Entry

In [2]: Entry.objects.create(title='hoge', content='foo')
Out[2]: <Entry: hoge>

In [3]: Entry.objects.create(title='fuga', content='bar')
Out[3]: <Entry: fuga>

In [4]: Entry.objects.create(title='fizz', content='buzz')
Out[4]: <Entry: fizz>

まずは"manage.py shell"でtitleが"hoge"、"fuga"、"fizz"でcontentが"foo"、"bar"、"buzz"のデータを作っておきます。
複雑な照合はQオブジェクトを使うと簡単にできます。reduce*1でQオブジェクトを結合するつもりなのでoperatorもインポートしておきます。

In [5]: from django.db.models import Q

In [6]: import operator

よくある検索のパターンですが、"hoge fuga"という文字列からtitleが"hoge"または"fuga"であるクエリセットを得てみます。

In [7]: Entry.objects.filter(reduce(operator.or_, [Q(title=keyword) for keyword in "hoge fuga".split(' ')]))
Out[7]: [<Entry: hoge>, <Entry: fuga>]

"hoge fuga"をsplitで分解してkeywordに入れ、Qオブジェクトにしたものをリストにし、Qオブジェクトをor演算でつなげてfilterに渡します。
DjangoのQオブジェクトはこのように通常のPython演算子で結合できるので、さらに複雑な条件でも簡単に作成することができます。
titleの先頭が"fi"もしくは、titleかcontentに"o"を含むクエリセットを得てみます。

In [10]: Entry.objects.filter(Q(title__startswith='fi') | Q(title__contains='o') | Q(content__contains='o'))
Out[10]: [<Entry: hoge>, <Entry: fizz>]

このような複雑なクエリでもSQLの知識なしに簡単に扱えます。すばらしい。

*1:Rubyで言うところのinject