データベース API リファレンス
| revision-up-to: | 7193 (0.97pre SVN) |
|---|
データモデル を作成したら、次はデータベースからデータを取り出す必要があ ります。このドキュメントでは、モデルから利用できるデータベース抽象化 API と、 オブジェクトを生成、取得、更新する方法について説明します。
このリファレンスでは、以下のような Poll アプリケーションを参考に話を進めま す:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
def __unicode__(self):
return self.headline
オブジェクトの生成
Django では、データベーステーブル上のデータを Python オブジェクトで表現する ために、モデルクラスがデータベーステーブルを表現し、クラスのインスタンスが テーブル上のレコードを表現するという直感的なシステムを使っています。
オブジェクトを生成するには、キーワード引数を使ってモデルクラスのインスタン スを生成し、 save() メソッドを呼び出してデータベースに保存します。
モデルクラスは Python パス上のどこからでも import でき、期待通りに動作しま す (わざわざこのような説明をするのは、以前のバージョンの Django ではモデル の import 方法がかなり風変わりだったからです)。
モデルが mysite/blog/models.py というファイルで定義されているとすると、 オブジェクトの作成は以下の例のようになります:
from mysite.blog.models import Blog b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') b.save()
この操作によって、背後では INSERT SQL 文が実行されます。 Django はユー ザが明示的に save() を呼び出すまでデータベースを操作しません。
ワンステップでオブジェクトを生成して保存するには create メソッドを使 います。
主キーの自動インクリメント
モデルに AutoField 、すなわち自動インクリメントされる主キーがある場合に は、オブジェクトに対して最初に save() を呼び出したときに自動インクリメ ント値が計算され、保存されます。
例えば:
b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.') b2.id # b には ID がないので None を返します。 b2.save() b2.id # 新たに保存されたオブジェクトの ID を返します。
ID の値は Django ではなくデータベースによって計算されるので、 save() を 呼び出すまでは ID の値は分かりません。
(利便性のため、明示的に primary_key=True を指定したフィールドを作成しな いかぎり、デフォルトでは各モデルに id という名前の AutoField が追加されます。詳しくは AutoField のドキュメント を参照してください。)
自動主キーの値を明示的に指定する
モデルが AutoField を持っていて、新たなオブジェクトの ID を保存時に明示 的に指定したい場合、 ID を自動的に決定させずに保存前に明示的に指定してくだ さい。
例えば:
b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.') b3.id # Returns 3. b3.save() b3.id # Returns 3.
自動主キーの値を手動で割り当てる場合、決して既に存在する主キーの値を割り当 てないようにしてください! 明示的な主キー値を持った新たなオブジェクトを作成 し、その主キーがすでにデータベース上に存在する場合、 Django は保存操作を新 たなオブジェクトの作成ではなく、既存のオブジェクトの変更とみなします。
上の 'Cheddar Talk' ブログを例にとると、以下の例はデータベース上の既存 のレコードをオーバライドしてしまいます:
b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.') b4.save() # Overrides the previous blog with ID=3!
この理由については後述の UPDATE と INSERT の区別 を参照してください。
主キーの衝突がないとはっきり判っている場合なら、自動主キーの値の明示的な指 定は大量のオブジェクトを保存する際にきわめて便利です。
オブジェクトの保存時に何が起きるのか
Django は以下の段階を踏んでオブジェクトを保存します:
``pre_save`` シグナルの発行 シグナルの発行によって、何らかのオブ ジェクトを保存しようとしていることを通知します。リスナ (listener) を 用意して登録しておけ、 pre_save シグナルが発行された時に実行でき ます (シグナルのドキュメントはまだありません)。
データの前処理 オブジェクトの各フィールドについて、保存時に自動 的に実行する必要があるデータ修飾処理がないか調べ、あれば実行します。
ほとんどのフィールドは前処理を 伴いません 。フィールドのデータは そのまま保存されます。前処理が行われるのは、特殊な挙動を示すフィー ルドだけです。例えば、 auto_now=True に設定された DateField の場合、前処理の段階で、フィールドの内容が現在の日付になるようデータ を置き換えます (現時点では、「特殊な」挙動を示すフィールドのリストを 全て列挙したドキュメントはありません)。
データベース保存用のデータ準備処理 各フィールドについて、フィー ルドの現在の値を元にデータベースに保存できる型のデータを生成します。
ほとんどのフィールドはデータ準備処理を 伴いません 。整数や文字列は Python オブジェクトとして「いつでもデータベースへの書き込みに使える」 形式になっています。ただ、より複雑なデータ型の場合、なにがしかの修飾 が必要なことがあります。
例えば、 DateField は、データの保存に Python の datetime 型 を使います。データベースは datetime オブジェクトを保存しないので、 データベースに保存するには、フィールドの値を ISO 準拠の日付文字列に 変換せねばなりません。
データベースへの保存 前処理と準備処理を経たデータが SQL 文に組み 込まれ、データベースに挿入されます。
``post_save`` シグナルの発行 pre_save シグナルと同じく、オブ ジェクトが成功理に保存されたことを通知するために post_save シグ ナルが発行されます (シグナルのドキュメントはまだありません)。
生データの保存
開発版の Django で新たに追加された機能です
前処理段階 (全節の項目 #2) は便利ですが、フィールドに保存されているデータを 勝手に修飾してしまいます。指定したデータをそのまま保存させたい場合には、こ の仕様が問題を引き起こすかもしれません。
例えば、テストを実行するために何らかのテスト条件を構築するなら、テスト条件 に再現性をもたせる必要があるでしょう。前処理が実行されると、テスト条件に使 うデータが修飾されて、テストの実行ごとに違ったテスト条件をもたらしてしまう かも知れません。
こうした問題を防ぐには、オブジェクトの保存時に前処理を抑制する必要がありま す。前処理を抑制するには、 save() メソッドに raw=True 引数を指定し て、 生データを保存 します:
b4.save(raw=True) # オブジェクトを保存しますが、前処理は行いません
生データの保存を行うと、保存時に実行されるたいていの前処理を行いません。 ただし、 (pre_save シグナルの発行、データの準備処理、データベースへの挿 入処理、 post_save シグナルの発行といった) 前処理以外の処理は通常通り実 行されます。
「生データ保存」の使いどころ
ざっくり言えば、生データ保存はまず使う必要はないでしょう。フィールドの前 処理抑制は、再現性のあるテスト環境の構築のように、よほど変わった状況でな いかぎり使うべきでない極端な手段なのです。
オブジェクトへの変更を保存する
すでにデータベース上にあるオブジェクトへの変更を保存するには save() を 使います。
Blog インスタンス b5 がすでにデータベース上にあるとすると、以下の例 は b5 の名前を変更して、データベース上のレコードを更新します:
b5.name = 'New name' b5.save()
この例では、背後で UPDATE SQL 文が実行されています。 Django は明示的に save() を呼び出すまでデータベースを操作しません。
save() メソッドには戻り値がありません。
ForeignKey や ManyToManyField の保存
ForeignKey フィールドの更新は、通常のフィールドへの変更と同じです。すな わち、適切な型のオブジェクトを代入して保存すると、フィールドの値を更新でき ます:
cheese_blog = Blog.objects.get(name="Cheddar Talk") entry.blog = cheese_blog entry.save()
ManyToManyField の更新は少し違います。リレーションにレコードを一つ追加 したい場合は add() メソッドを使います:
joe = Author.objects.create(name="Joe") entry.authors.add(joe)
間違った型のオブジェクトを外部キーに代入したり add() したりすると Django はエラーを出します。
UPDATE と INSERT の区別
Django データベースオブジェクトがオブジェクトの作成と変更に同じ save() メソッドを使っていることにお気づきかもしれませんね。 Django は INSERT と UPDATE SQL 文のどちらを使うべきかの判断を抽象化しています。具体的 に言うと、 save() を呼び出したときに、Django は以下のアルゴリズムに従い ます:
- オブジェクトの主キー属性の評価値が False でない場合 (None や 空文字列の場合などでない場合) 、 Django は SELECT クエリを使って、 該当する主キーを持つレコードが存在するかどうか調べます。
- 該当する主キーを持つレコードがデータベース上に存在する場合には UPDATE クエリを使います。
- オブジェクトの主キー属性が設定 されていない 場合や、主キーが設定さ れているが該当するレコードは存在しない場合、 INSERT を使います。
新たなオブジェクトを保存する際、まだ使われていない値を主キーに指定できる保 証がないかぎり、主キーの値を明示的に指定しないよう注意してください。詳しく は上記の 自動主キーの値を明示的に指定する を参照してください。
オブジェクトの取得
オブジェクトをデータベースから取得するには、モデルクラスのマネジャ (Manager) を介してクエリセット (QuerySet) を構築します。
クエリセットはデータベース上にあるオブジェクトの集まりを表現しています。 クエリセットには、集合を指定パラメタに従って絞り込むための条件である フィルタ (filter) がゼロ個から複数個あります。 SQL 用語でいえば、 クエリセットは SELECT 文であり、フィルタは WHERE や LIMIT のような限定節にあたります。
クエリセットはモデルのマネジャから取得します。モデルには最低一つの マネジャがあり、デフォルトでは objects という名前がついています。 マネジャにはモデルクラスから直接アクセスしてください:
Blog.objects # <django.db.models.manager.Manager object at ...> b = Blog(name='Foo', tagline='Bar') b.objects # AttributeError: "Manager isn't accessible via Blog instances."
(「テーブルレベル」の操作と「レコードレベル」の操作を分離させるため、マネジャ はモデルのインスタンスではなくモデルクラスだけからアクセスできるようになっ ています。)
モデル内でのクエリセットの主なソースはマネジャです。マネジャは、データベー スオブジェクト上の全てのオブジェクトを表す「ルートの」クエリセットであるか のように振舞います。例えば、初期クエリセットである Blog.objects には、 データベース上の全ての Blog オブジェクトが入っています。
全てのオブジェクトの取得
テーブルからオブジェクトを取得する最も単純な方法では、全てのオブジェクトを 取得します。マネジャの all() メソッドを使って下さい。
例えば:
all_entries = Entry.objects.all()
all() メソッドはデータベース上の全てのオブジェクトを表現するクエリセッ トを返します。
(Entry.objects がクエリセットを返すというのなら、なぜ単に Entry.objects と書かないのでしょうか?それは、ルートのクエリセットであ る Entry.objects が特別扱いされていて、値評価できないようになっているか らです。 all() メソッドは、値評価 できる クエリセットを返します。
オブジェクトのフィルタ操作
マネジャによって提供されるクエリセットを使えば、データベーステーブル上の全 てのオブジェクトを表せます。とはいえ、通常は全オブジェクトの集合からサブセッ トだけを取り出したいことでしょう。
サブセットを作成するには、フィルタ条件を追加して、初期クエリセットをリファ インする必要があります。クエリセットの洗練には、主に二つの方法があります:
- filter(**kwargs)
- 指定した照合パラメタに一致するオブジェクトの集合を表現する、新たなクエ リセットを返します。
- exclude(**kwargs)
- 指定した照合パラメタに一致 しない オブジェクトの集合を表現する、新た なクエリセットを返します。
照合パラメタ (上の関数定義における **kwargs) は、後述の フィールドの照合 で解説するフォーマットにせねばなりません。
例えば、 2006 年のブログエントリを表すクエリセットを取得するには、以下のよ うに filter() を使います:
Entry.objects.filter(pub_date__year=2006)
(Entry.objects.all().filter(...) のように、 all() を使わなくてもよ いことに注意して下さい。 all() を使っても問題なく動作しますが、 all() が必要となるのはルートクエリセットから全てのオブジェクトを取り出 したい場合だけです。)
フィルタの連鎖
クエリセットをリファインした結果は、それ自体クエリセットになります。従って、 リファイン操作は連鎖させられます。例えば:
Entry.objects.filter(
headline__startswith='What').exclude(
pub_date__gte=datetime.now()).filter(
pub_date__gte=datetime(2005, 1, 1))
上の例は、データベースの全てのエントリを表す初期クエリセットに対し、 filter() をかけた後に exclude() を実行し、さらにもう一つ filter() をかけています。最終的に得られるのは、 "What" で始まる ヘッドラインのうち、 January 1, 2005 から今日までの間に公開されたエントリに なります。
フィルタしたクエリセットは一意になる
クエリセットのリファインを行うと、その都度新たなクエリセットを得ます。新た なクエリセットは以前のクエリセットになんら縛られていません。リファイン操作 のたびに、別個の独立したクエリセットが作成され、個別に保存したり、再利用し たりできます。
例えば:
q1 = Entry.objects.filter(headline__startswith="What") q2 = q1.exclude(pub_date__gte=datetime.now()) q3 = q1.filter(pub_date__gte=datetime.now())
これら 3 つのクエリセットは別個のものです。最初はヘッドラインが "What" で始 まる全てのエントリの入ったベースのクエリセットです。二つ目のクエリセットは、 最初のクエリセットのサブセットであり、 pub_date の値が現在時刻よりも大 きいものを排除します。三つ目のクエリセットも最初のクエリセットのサブセット で、 pub_date の値が現在時刻よりも大きいものだけを選択するようになって います。こうしたリファイン操作は、初期クエリセット (q1) に影響を及ぼし ません。
クエリセットは遅延評価される
クエリセットの評価は遅延型 (lazy) です。すなわち、クエリセットの作成自体は データベース操作を引き起こしません。クエリセットは 評価される までデータ ベースへのクエリを実行しないので、延々フィルタを重ねられます。
クエリセットはいつ評価されるのか
以下の方法を使うと、クエリセットを評価できます:
イテレーション。 クエリセットはイテレーション可能オブジェクトであ り、オブジェクトに対して最初にイテレーション操作を行ったときにデータ ベースクエリを実行します。例えば、以下の例はデータベース中の全てのエ ントリのヘッドラインを出力します:
for e in Entry.objects.all(): print e.headlineスライス。 クエリセットに制約を課す の節で説明しているように、 Python の配列スライス表記を使うとクエリセットをスライスできます。通常、 クエリセットに対するスライスは (未評価の) 別のクエリセットを返します が、スライス表記に「ステップ (step)」パラメタを使った場合には、データ ベースクエリを実行します。
repr(). クエリセットに対して repr() を呼び出すと、クエリセッ トは値評価されます。これは Python 対話インタプリタでの利便性のための 仕様で、 API を対話的に使うときに結果を即座に見られるようにしています。
len(). クエリセットに対して len() を呼び出すと、クエリセッ トは値評価されます。予想に違わず、 len() はクエリ結果リストの長さ を返します。
注意: クエリセット中のレコードの数を知りたいだけなら、 len() は 使わないでください 。レコード数の計算はデータベース上で SQL 文の SELECT COUNT(*) 使って行う方が遥かに効率的であり、まさにその理由 から Django では count() メソッドを提供しています。後述の count() を参照してください。
list(). クエリセットに対して list() を呼び出すと、値評価を強 制できます。例えば:
entry_list = list(Entry.objects.all())とはいえ、この方法を使うと、Django が全ての要素のリストをメモリ上にロー ドするため、巨大なメモリオーバヘッドを引き起こす可能性があるので十分 注意してください。これに対し、クエリセットに対するイテレーション操作 では、必要な分だけデータをロードしてオブジェクトをインスタンス化する という利点があります。
クエリセットに制約を課す
クエリセットの返す結果を特定の個数に制限したい場合には、配列スライス表記を 使います。これは SQL の LIMIT 節や OFFSET 節と等価になります。
以下の例は、最初の 5 オブジェクトだけを返します (LIMIT 5 に相当します):
Entry.objects.all()[:5]
以下の例は、 6 番目から 10 番目までのオブジェクトを返します (OFFSET 5 LIMIT 5 に相当します):
Entry.objects.all()[5:10]
一般に、クエリセットのスライスはクエリセットを返し、クエリの評価は行いませ ん。例外はスライス表記に「ステップ (step)」パラメタを使ったときです。以下の 例では、クエリを実際に実行し、最初の 10 オブジェクト中から 1 つおきにオブジェ クトを取り出したリストを返します:
Entry.objects.all()[:10:2]
リストではなく 単一の オブジェクトを取得したい場合 (SELECT foo FROM bar LIMIT 1 のような場合) には、スライスではなく単純な インデクス指定を行います。以下の例はデータベースのエントリをヘッドラインに ついてアルファベット順に整列した後、最初の Entry を取得して返します:
Entry.objects.order_by('headline')[0]
これはだいたい以下と同じになります:
Entry.objects.order_by('headline')[0:1].get()
ただし、指定条件にマッチするオブジェクトがない場合、前者は IndexError, 後者は DoesNotExist を送出します。
新たなクエリセットを返すクエリセットメソッド
Django では、クエリセットの返す結果の形式や、 SQL クエリの実行方法を変更す るためのリファインメソッドを幅広く提供しています。
filter(**kwargs)
指定の照合パラメタに一致するオブジェクトの入った新たなクエリセットを返しま す。
照合パラメタ (**kwargs) は後述の フィールドの照合 で説明するフォーマッ トにします。複数のパラメタを指定すると、背後の SQL 文では AND で結合さ れます。
exclude(**kwargs)
指定の照合パラメタに一致 しない オブジェクトの入った新たなクエリセットを 返します。
照合パラメタ (**kwargs) は後述の フィールドの照合 で説明するフォーマッ トにします。複数のパラメタを指定すると、背後の SQL 文では AND で結合さ れ、制約条件節全体を NOT() で囲みます。
以下の例では、 pub_date が 2005 年 1 月 3 日より未来の日時になっていて、 かつ headline が "Hello" で始まる全てのエントリを除外します:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
SQL 用語でいうと、以下のようなクエリの評価になります:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
また、以下の例では、 pub_date が 2005 年 1 月 3 日より未来の日時で あるか、 または headline が "Hello" で始まる全てのエントリを除外しま す:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
SQL 用語でいうと、以下のようなクエリの評価になります:
SELECT ... WHERE NOT pub_date > '2005-1-3' AND NOT headline = 'Hello'
二つ目の例の方が厳しい制約になっていることに注意して下さい。
order_by(*fields)
デフォルトでは、 QuerySet の返す結果はモデルの Meta の ordering オプションに指定した整列条件のタプルに従って整列されます。 order_by を 使うと、この挙動を QuerySet 単位でオーバライドできます。
例えば:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
このクエリの結果は、まず pub_date で降順に並べ、次いで headline で 昇順に並べたものになります。 "-pub_date" の先頭にあるマイナス記号が 降順 表しています。何も指定しないと昇順になります。整列をランダムに したければ、以下のように "?" を使います:
Entry.objects.order_by('?')
注意: order_by('?') を使うと、使っているバックエンドによってはコストの かかる低速なクエリになります。
別のテーブルのフィールド値で整列させる場合、テーブル名をドット表記で指定し ます:
Entry.objects.order_by('blogs_blog.name', 'headline')
大小文字の区別を考慮して整列するかどうかを指定する方法はありません。大小文 字の区別については、 Django は現在使っているデータベースバックエンドの整列 方法に従います。
distinct()
SQL クエリに SELECT DISTINCT を使う新たな QuerySet を返します。 distinct() を使うと、クエリ結果から重複する行をなくします。
デフォルトでは、 QuerySet は重複する行を除去しません。通常は、 Blog.objects.all() のような単純なクエリは重複する行を含むような結果にな らないため、これはあまり問題にはなりません。
しかし、クエリが複数のテーブルにわたる場合、 QuerySet の評価結果に重複 する結果が入る場合があります。その場合には distinct() を使って下さい。
values(*fields)
ValueQuerySet を返します。 ValueQuerySet は QuerySet のサブクラ スで、評価結果としてモデルインスタンスオブジェクトの代りに辞書のリストを返 す QuerySet です。
リスト中の各辞書は個々のオブジェクトを表現しており、キーがモデルオブジェク トの各属性名に、対応しています。
以下の例では、 values() の辞書と通常のモデルオブジェクトを比較していま す:
# リストには Blog オブジェクトが入ります。
>>> Blog.objects.filter(name__startswith='Beatles')
[Beatles Blog]
# リストには辞書が入ります。
>>> Blog.objects.filter(name__startswith='Beatles').values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]
values() オプションの可変長の引数 *fields を取れます。このオプションは SELECT の制限に使うフィールド名を列挙したものです。 fields を指定し た場合、辞書には指定した名前のフィールドのキーと値だけが入ります。 *fields を指定しなければ、辞書にはテーブルの全てのフィールドのキーと値 が入ります。
例を示します:
>>> Blog.objects.values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}],
>>> Blog.objects.values('id', 'name')
[{'id': 1, 'name': 'Beatles Blog'}]
ValuesQuerySet が便利なのは、わずかな数のフィールドの値しか必要でなく、 モデルインスタンスオブジェクトの機能が必要でないと分かっている場合です。 必要なフィールドだけを選択すると、さらに効率的になります。
最後に、 ValuesQuerySet は QuerySet のサブクラスなので、 QuerySet の全てのメソッドを持っている点に注意してください。 ValuesQuerySet に対して filter() や order_by() といった操作を行 えます。そう、以下の二つの呼び出しは等価になります:
Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()
Django の作者たちは、全ての SQL 関係のメソッドを先に配置し、その後に (必要 なら) 出力関係のメソッド (values() など) を配置するやり方を好んでいます。 とはいえ、これは実際上問題ではないので、個人的な信条を反映させてかまいませ ん。
dates(field, kind, order='ASC')
DateQuerySet を返します。 DateQuerySet は QuerySet のサブクラス で、評価結果としてクエリセット内のコンテンツの全日付を datetime.datetime オブジェクトとして返す QuerySet です。
field はモデルの DateField または DateTimeField の名前です。
kind は "year", "month" または "day" です。 結果リスト中の各 datetime.datetime オブジェクトは type の指定に従っ て切り詰められます。
- "year" フィールドの年部分の値の重複しないリストを返します。
- "month" フィールドの年/月部分の値の重複しないリストを返します。
- "day" フィールドの年/月/日部分の値の重複しないリストを返します。
order には結果の並び順を指定します。デフォルト値は 'ASC' で、 "ASC" または "DESC" にできます。
例を示します:
>>> Entry.objects.dates('pub_date', 'year')
[datetime.datetime(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.datetime(2005, 3, 20)]
none()
開発版の Django で新たに追加された機能です
EmptyQuerySet を返します。 EmptyQuerySet とは、評価結果が常に空の リストになるクエリセットです。関数の戻り値などで空の照合結果を返したいが、 呼び出し側が (空のリストなどではなく) クエリセットオブジェクトの戻り値を期 待している場合に便利です。
例:
>>> Entry.objects.none() []
select_related()
自動的に外部キーのリレーションを「追跡」し、クエリを実行したときにリレーショ ン先のオブジェクトも加えて選択するような QuerySet を返します。 これはパフォーマンスを向上させるための機構で、クエリは (ときに非常に) 巨大 になりますが、以後の外部キーへのリレーションでデータベースクエリが必要なく なります。
以下の例では、通常の照合と select_related() を使った照合との違いを比較 しています。通常の照合は以下のようになります:
# データベースを操作します。 e = Entry.objects.get(id=5) # リレーション先の Blog オブジェクトを取得するために再度データベースを # 操作します。 b = e.blog
select_related() を使った照合は以下のようになります:
# データベースを操作します。 e = Entry.objects.select_related().get(id=5) # e.blog は上のクエリで取得済みなので、データベースを操作しません。 b = e.blog
select_related は可能な限り外部キーを追跡することに注意してください。以 下のようなモデル:
class City(models.Model):
# ...
class Person(models.Model):
# ...
hometown = models.ForeignKey(City)
class Book(models.Model):
# ...
author = models.ForeignKey(Person)
の場合、 Book.objects.select_related().get(id=4) を実行すると、リレーションの張られた Person に加えて City もキャッ シュします:
b = Book.objects.select_related().get(id=4) p = b.author # Doesn't hit the database. c = p.hometown # Doesn't hit the database. b = Book.objects.get(id=4) # No select_related() in this example. p = b.author # Hits the database. c = p.hometown # Hits the database.
select_related() は null=True であるような外部キーカラムは追跡しな いので注意してください。
通常、 select_related() を使うと、データベースの呼び出し回数を減らせる ので、大幅にパフォーマンスを向上できます。しかし、リレーションが深くネスト しているような状況では、 select_related() が追跡するリレーションが「多 すぎる」ために、巨大なクエリを生成してしまい、結果的にパフォーマンスの低下 を招く場合があります。
こうした状況に対応するため、 select_related() に depth 引数を指定す ると、以下の例のようにリレーションを何「レベル」まで追跡するかを制御できま す:
b = Book.objects.select_related(depth=1).get(id=4) p = b.author # 追跡済みのリレーション。データベースを操作しません。 c = p.hometown # 未追跡のリレーション。データベースを呼び出します。
depth 引数は開発版の Django で新たに追加された機能です。
extra(select=None, where=None, params=None, tables=None)
時として、 Django のクエリ表記法だけでは複雑な WHERE 節を容易に表現でき ない場合があります。こうした特異な場合のために、 Django では extra() というクエリセット修飾子を提供しています。このメソッドは、クエリセットが 生成する SQL 文中に特定の SQL 節を挿入するためのフックです。
定義上、これらの拡張照合機能は (直接 SQL コードを書いているため) データベー スエンジン間の可搬性がありません。また、 DRY 則の侵犯でもあります。可能な限 り使わないようにして下さい。
params, select, where, tables のいずれかを指定します。 いずれの引数も必須ではありませんが、少なくとも一つは指定せねばなりません。
- select
select キーワードを使うと、 SELECT 節に追加のフィールドを選択で きます。この引数は、属性名とその属性値を計算するための SQL 節を対応づけ た辞書にします。
例えば:
Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})のようにすると、 Entry オブジェクトは、エントリの pub_date が Jan. 1, 2006 より大きいかどうかを示すブール値の属性 is_recent を 持つようになります。
Django は指定された SQL を直接 SELECT 文に挿入するので、上の例の SQL 文は以下のようになります:
SELECT blog_entry.*, (pub_date > '2006-01-01') FROM blog_entry;
次の例はもっと高度です。この例では、 Blog オブジェクトに関連づけら れている Entry オブジェクトの個数を表す整数を、 Blog オブジェク トの entry_count 属性に持たせるためにサブクエリを実行しています:
Blog.objects.extra( select={ 'entry_count': 'SELECT COUNT(*) FROM blog_entry ' \ 'WHERE blog_entry.blog_id = blog_blog.id' }, )(上の場合では、クエリの FROM 節に blog_blog が入るという事実を 利用しています。)
上の例の場合、 SQL は以下のようになります:
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) FROM blog_blog;
ほとんどのデータベースエンジンでは、サブセレクションの周りに丸括弧が必 要ですが、Django の select 節では必要ないということに注意してくださ い。また、 MySQL の一部のバージョンのように、データベースバックエンドに よってはサブクエリをサポートしないので注意してください。
- where / tables
明示的に追加の WHERE 節を渡す必要がある場合 -- おそらく非明示的な結 合を行っている場合 -- には、 where キーワードを使って下さい。 tables を使えば、 SQL の FROM 節に手動でテーブル名を追加できま す。
where や tables は、ともに文字列のリストを引数にとります。 where パラメタの内容は全て、多の検索条件と "AND" で結合されます。
例えば:
Entry.objects.extra(where=['id IN (3, 4, 5, 20)'])
は、(大雑把にいって) 以下のような SQL 文に変換されます:
SELECT * FROM blog_entry WHERE id IN (3, 4, 5, 20);
params
上で説明した select や where パラメタでは、標準の Python の文字 列プレースホルダ '%s' を使って、データベースエンジンが自動的にパラ メタをクオートするよう指示できます。 params 引数には、プレースホル ダで置き換えられるパラメタのリストを指定します。
例えば:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])select や where の中に直接値を埋め込まず、常に params を使 うようにしてください。というのも、 params を使えば、バックエンド固 有の方法でパラメタの値を正しくクオートするからです。 (例えば引用符文字 などを正しくエスケープします)
悪い例:
Entry.objects.extra(where=["headline='Lennon'"])良い例:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])select や where の中に入れるプレースホルダの個数合計と、 params リストに入れる値の個数は一致させねばなりません。
QuerySet を返さないクエリセットメソッド
以下のクエリセットメソッドは、クエリセットを評価して、クエリセット でない 値を返します。
これらのメソッドはキャッシュを使わず (後述の キャッシュとクエリセット を 参照してください)、メソッド呼び出しごとにデータベースにクエリをかけます。
get(**kwargs)
照合パラメタに一致するオブジェクトを返します。照合パラメタは後述の フィールドの照合 で説明するフォーマットにします。
複数のオブジェクトが見つかった場合には AssertionError を送出します。
指定パラメタに対するオブジェクトが見つからなかった場合には DoesNotExist 例外を送出します。 DoesNotExist 例外はモデルクラスの属性の一つです。 例えば:
Entry.objects.get(id='foo') # raises Entry.DoesNotExist
DoesNotExist 例外は django.core.exceptions.ObjectDoesNotExist を継 承しているので、複数の DoesNotExist 例外を except: のターゲットにで きます。例えば:
from django.core.exceptions import ObjectDoesNotExist
try:
e = Entry.objects.get(id=3)
b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
print "Either the entry or blog doesn't exist."
create(**kwargs)
ワンステップでオブジェクトを生成して保存するための便宜メソッドです。 すなわち、以下の文:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
と、以下の文:
p = Person(first_name="Bruce", last_name="Springsteen") p.save()
は等価です。
get_or_create(**kwargs)
kwargs に指定したオブジェクトを照合し、なければ生成するための便宜メソッドで す。
(object, created) の形式のタプルを返します。 object は取得または作 成されたオブジェクトであり、 created はブール値で、オブジェクトが新たに 生成されたかどうかを示します。
このメソッドは、お決まりのコードを書く上でのショートカットとして定義されて おり、データを取り込むスクリプトを書くときに便利です。例えば:
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
このようなコードパターンでは、モデル中のフィールドが増えると手に負えなくな ります。 get_or_create() を使うと、上のコード例は以下のように書き直せま す:
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)})
get_or_create() に渡されたキーワード引数は、 (オプションの引数である defaults を除いて) 全て get() の呼び出し時の引数として渡されます。 オブジェクトが見つかった場合、 get_or_create() は見つかったオブジェクト と False を返します。オブジェクトが 見つからなかった 場合、新たに生成 されたオブジェクトと True を返します。新たなオブジェクトは以下のアルゴ リズムで作成されます:
defaults = kwargs.pop('defaults', {})
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
obj = self.model(**params)
obj.save()
上のコードを日本語で表すなら、まず 'defaults' でないキーワード引数のう ち、二重アンダースコアを含まないもの (二重アンダースコアはあいまい照合のキー ワードなので除外します) を使ってパラメタ params を作成し、必要に応じて デフォルト値 defaults で内容を更新して、その結果をモデルクラスを呼び出 すときのキーワード引数に使います。
defaults という名前のフィールド名を持っていて、 get_or_create() の 中で厳密照合に使いたければ、以下のように 'defaults__exact' を使います:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
最後に、 Django ビューの中で get_or_create() を使う場合についてひとこと 注意しておきましょう。上で説明したように、主として get_or_create() が有 用なのは、データを解析し、該当する既存のデータが存在しない場合に新たなレコー ドを生成するようなスクリプトを書く場合です。ビューで get_or_create() を 使いたいのなら、特に理由のない限り POST リクエスト中で使うようにしましょ う。一般論として、 GET リクエストの処理中ではデータに影響を及ぼすべきで はありません。データに副作用をもたらすようなページのリクエストには常に POST を使うようにしましょう。詳しくは、 HTTP 仕様における 安全なメソッド を参照してください。
count()
クエリセットに一致するデータベース上のオブジェクトの個数を表す整数を返しま す。 count() は例外を送出しません。
例えば:
# データベース中のエントリの総数を返します。 Entry.objects.count() # ヘッドラインが 'Lennon' を含むエントリの総数を返します。 Entry.objects.filter(headline__contains='Lennon').count()
count() は背後で SELECT COUNT(*) を実行するので、単にオブジェクトの 個数を数えたい場合には、全てのレコードを Python オブジェクトとしてロードし てから len() を呼び出すのではなく、常に count() を使うようにしてく ださい。
(PostgreSQL や MySQL といった) どのデータベースを使っているかによって、戻り 値が Python の通常の整数型ではなく、長整数になることもあります。これは実装 上の問題であり、現実的に問題になることはありません。
in_bulk(id_list)
主キーの値のリストを引数にとり、各主キー値とオブジェクトを対応づけた辞書を 返します。
例えば:
>>> Blog.objects.in_bulk([1])
{1: Beatles Blog}
>>> Blog.objects.in_bulk([1, 2])
{1: Beatles Blog, 2: Cheddar Talk}
>>> Blog.objects.in_bulk([])
{}
in_bulk() に空のリストを渡すと空の辞書を返します。
iterator()
QuerySet を評価し (クエリを実行し) て、その結果の入った イテレータ を返します。 QuerySet は通常、最初にアクセスした時点で、全ての検索結果 を読み込んで、対応するオブジェクトのインスタンスを生成してしまいます。 一方、 iterator() は、離散的なチャンク単位で検索結果を読み出し、オブジェ クトをインスタンス化して、一度に一つづつ yield します。 QuerySet は膨大 な数のオブジェクトを返す場合があるので、その場合には iterator() を使う ことでパフォーマンスを改善し、メモリの使用量を劇的に減らせます。
すでに値にアクセス済みの QuerySet に対して iterator() を呼び出すと、 値の評価が再度行われ、クエリが繰り返し発行されるので注意してください。
latest(field_name=None)
日付フィールドである field_name の値に応じて、テーブル中の最新のオブジェ クトを返します。
以下の例では、 pub_date フィールドに応じて、テーブル中の最新の Entry を返します:
Entry.objects.latest('pub_date')
モデルの Meta で get_latest_by を指定している場合、 latest() の field_name 引数は省略できます。 Django は get_latest_by に指定し たフィールドをデフォルト値にします。
get() と同様、 latest() は指定パラメタに一致するオブジェクトがない 場合に DoesNotExist を送出します。
latest() は純粋に利便性と可読性のためだけに存在しています。
フィールドの照合
フィールドの照合は、 SQL の WHERE 節の中身を決めます。フィールド照合は、 filter(), exclude() および get() といったクエリセットのメソッド のキーワード引数として指定します。
基本的に、照合のキーワード引数名は field__lookuptype=value のような形 式をとります (アンダースコアは二重です)。例えば:
Entry.objects.filter(pub_date__lte='2006-01-01')
は、(大雑把にいって) 以下のような SQL 文に変換されます:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
フィールド検索のからくり
Python には、任意の名前と値をもった引数を受け取れる関数を定義する機能が あり、引数名とその値は実行時に評価されます。くわしい情報は公式の Python チュートリアルの キーワード引数 を参照してください。
無効なキーワード引数を渡した場合、 TypeError を送出します。
データベース API では、以下の照合タイプをサポートしています:
exact
厳密な一致です、比較対象の値を None にすると、SQL における NULL と の比較として扱われます (詳しくは isnull を参照してください)。
例:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
等価な SQL 文:
SELECT ... WHERE id = 14; SELECT ... WHERE id = NULL;
iexact
大小文字の区別をしない一致です。
例:
Blog.objects.get(name__iexact='beatles blog')
等価な SQL 文:
SELECT ... WHERE name ILIKE 'beatles blog';
この例は、 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG' などにマッチします。
contains
大小文字を区別する包含テストです。
例:
Entry.objects.get(headline__contains='Lennon')
等価な SQL 文:
SELECT ... WHERE headline LIKE '%Lennon%';
この例では、 'Today Lennon honored' というヘッドラインには一致しますが、 'today lennon honored' には一致しません。
SQLite は大小文字を区別する LIKE をサポートしないので、 contains は icontains と同じになります。
icontains
大小文字を区別しない包含テストです。
例:
Entry.objects.get(headline__icontains='Lennon')
等価な SQL 文:
SELECT ... WHERE headline ILIKE '%Lennon%';
gte
等しいか、より大きい値に一致します。
lt
より少ない値に一致します。
lte
等しいか、より少ない値に一致します。
in
指定のリストに入っているものに一致します。
例:
Entry.objects.filter(id__in=[1, 3, 4])
等価な SQL 文:
SELECT ... WHERE id IN (1, 3, 4);
startswith
大小文字を区別する starts-with です。
例:
Entry.objects.filter(headline__startswith='Will')
等価な SQL 文:
SELECT ... WHERE headline LIKE 'Will%';
SQLite は大小文字を区別する LIKE をサポートしないので、 startswith は istartswith と同じになります。
istartswith
大小文字を区別しない starts-with です。
例:
Entry.objects.filter(headline__istartswith='will')
等価な SQL 文:
SELECT ... WHERE headline ILIKE 'Will%';
endswith
大小文字を区別する ends-with です。
例:
Entry.objects.filter(headline__endswith='cats')
等価な SQL 文:
SELECT ... WHERE headline LIKE '%cats';
SQLite は大小文字を区別する LIKE をサポートしないので、 endswith は iendswith と同じになります。
iendswith
大小文字を区別しない ends-with です。
例:
Entry.objects.filter(headline__iendswith='will')
等価な SQL 文:
SELECT ... WHERE headline ILIKE '%will'
range
範囲テスト (閉包テスト) です。
例:
start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
等価な SQL 文:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
range は日付、数値、文字など、SQL で BETWEEN を使える場所ならどこで も使えます。
year
date/datetime フィールドにおける、 year の厳密一致です。
例:
Entry.objects.filter(pub_date__year=2005)
等価な SQL 文:
SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';
(厳密な SQL シンタクスはデータベースエンジンによって違います。)
month
date/datetime フィールドにおける、 month の厳密一致です。 1 (1月) から 12 (12 月) までの整数を引数にとります。
例:
Entry.objects.filter(pub_date__month=12)
等価な SQL 文:
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
(厳密な SQL シンタクスはデータベースエンジンによって違います。)
day
date/datetime フィールドにおける、 day の厳密一致です。
例:
Entry.objects.filter(pub_date__day=3)
等価な SQL 文:
SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
(厳密な SQL シンタクスはデータベースエンジンによって違います。) このクエリ文は、「1 月 3 日」や「7 月 3 日」のように、毎月 3 日、にマッチし ます。
isnull
True または False を引数にとり、それぞれが IS NULL および IS NOT NULL に対応しています。
例:
Entry.objects.filter(pub_date__isnull=True)
等価な SQL 文:
SELECT ... WHERE pub_date IS NULL;
__isnull=True vs __exact=None
__isnull=True と __exact=None には重要な違いがあります。 SQL の定義では NULL に等しい値は存在しないので、 __exact=None は 常に 空の結果セットを返すことになります。一方、 __isnull は、 指定フィールドに NULL 値が入っているかどうかを比較演算を行わずに決 定します。
search
全文インデクスを活用した全文検索で、ブール値を返します。このメソッドは contains に似ていますが、全文インデクスを使うためはるかに高速です。
この機能は MySQL でだけ利用可能です。また、全文インデクスを追加するにはデー タベースを直接操作する必要があります。
regex
開発版の Django で新たに追加された機能です
正規表現による大小文字を区別した検索を行います。
正規表現の構文は各データベースバックエンドで使われているものと同じです。 正規表現による照合をサポートしない sqlite バックエンドの場合に、Python の re モジュールと同じ構文を使います。
使い方の例を示します:
Entry.objects.get(title__regex=r'^(An?|The) +')
同じ意味の SQL 文は以下のようになります:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
正規表現を指定する場合には raw 文字列を使う ('foo' でなく r'foo' を 使う)よう勧めます。
正規表現によるマッチングは ado_mssql バックエンドではサポートされていま せん。 ado_mssql で regex を使おうとすると NotImplementedError を送出します。
iregex
開発版の Django で新たに追加された機能です
正規表現による大小文字を区別しない検索を行います。
使い方の例を示します:
Entry.objects.get(title__iregex=r'^(an?|the) +')
同じ意味の SQL 文は以下のようになります:
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
デフォルトの照合形式は exact
照合形式 (lookup type) を指定しない場合、つまりキーワード引数に二重アンダー スコアが入っていない場合は、照合形式は exact であるとみなされます。
例えば、以下の二つの文は等価です:
Blog.objects.get(id__exact=14) # 明示的な形式 Blog.objects.get(id=14) # 暗黙で __exact を表す
これは exact 照合がよくある形式であることによる便宜的なものです。
pk 照合ショートカット
利便性のために、 Django には pk という照合形式があります。 pk は primary_key を表します。
Blog モデルの例では、主キーは id フィールドなので、以下の二つの文は 等価になります:
Blog.objects.get(id__exact=14) # 明示的な形式 Blog.objects.get(id=14) # 暗黙で __exact を表す Blog.objects.get(pk=14) # pk は暗黙で id__exact を表す
pk は __exact のクエリにしか使えないわけではありません。どのクエリ 用キーワードも pk と組み合わせてかまいません。 pk を使ったクエリは、 モデルの主キーに対するクエリになります。
# id が 1, 4 および 7 のブログエントリを取得する Blog.objects.filter(pk__in=[1,4,7]) # id > 14 の全てのブログエントリを取得する Blog.objects.filter(pk__gt=14)
pk による検索は join 越しでも行えます。 例えば、以下の二つの文は等価で す:
Entry.objects.filter(blog__id__exact=3) # 明示的な形式 Entry.objects.filter(blog__id=3) # 暗黙で __exact を表す Entry.objects.filter(blog__pk=3) # __pk は暗黙で __id__exact を表す
リレーションをまたいだ照合
Django では、背後で自動的に SQL JOIN を処理し、照合の際にリレーションを 「追跡」する、強力でありながら直感的な手段を提供しています。リレーションを またぐには、二重アンダースコアを使ってリレーションの張られたフィールドのフィー ルド名を指定します。リレーション間のスパンは、目的のフィールドに到達するま でいくらでも連鎖させられます。
以下の例では、 name が 'Beatles Blog' であるような Blog の Entry エントリオブジェクト全てを取得します:
Entry.objects.filter(blog__name__exact='Beatles Blog')
スパンは好きなだけ深く張れます。
リレーションのスパンは逆方向にも張れます。「逆方向の」リレーションを参照す るには、モデル名を小文字にした名前を使います。
以下の例では、 headline に 'Lennon' を含むような Entry を少なく とも一つ持つような全ての Blog オブジェクトを取得します:
Blog.objects.filter(entry__headline__contains='Lennon')
LIKE 文におけるパーセント記号とアンダースコアのエスケープ
LIKE を使う SQL 文になるようなフィールド照合メソッド (iexact, contains, icontains, startswith, istartswith, endswith, iendswith) では、 LIKE 文で使われる二つの特殊な文字、すなわちパーセ ント記号とアンダースコアを自動的にエスケープします。 (LIKE 文では、パー セント記号は任意の複数文字に対するワイルドカードを表し、アンダースコアは任 意の一文字に対するワイルドカードを表します。)
この機能によって、照合操作を直感的に行え、データベースの抽象化を守れます。 例えば、パーセント記号を含むようなエントリ全てを取得したければ、以下のよう にパーセント記号をそのまま使います:
Entry.objects.filter(headline__contains='%')
Django はクオートの処理に気を配ってくれます。 SQL は以下のような感じになり ます:
SELECT ... WHERE headline LIKE '%\%%';
アンダースコアについても同じようなエスケープを行います。パーセント記号とア ンダースコアはいずれも透過的に処理されます。
キャッシュとクエリセット
データベースへのアクセスを最小限にとどめるため、クエリセット各々にはキャッ シュがあります。効率的なコードを書く上で、キャッシュのからくりを理解してお くのは重要なことです。
クエリセットが新たに生成された時点では、キャッシュは空です。クエリセットを 最初に評価したとき (すなわち、データベースへのクエリが最初に生じたとき)、 Django はクエリ結果をクエリセットオブジェクト内のキャッシュに保存し、明示的 にリクエストした結果だけ (例えば、クエリセットに対してイテレーション操作を する場合には、結果セットの最初の要素) を返します。それ以後は、クエリセット を際利用するとキャッシュ済みの結果を返します。
このキャッシュの挙動をよく覚えておいて下さい。というのも、クエリセットを正 しく扱わないと、おもわぬところで手を噛まれるはめになるからです。例えば、以 下の例では二つのクエリセットを作成し、値を評価して、すぐにクエリセットを捨 ててしまっています:
print [e.headline for e in Entry.objects.all()] print [e.pub_date for e in Entry.objects.all()]
これはすなわち全く同じデータベースクエリが二度実行され、データベースの負荷 を倍加させることを示します。また、 Entry は二つのリクエストを処理する間 にも追加されたり削除されたりする可能性があるため、二つのリストには必ずしも 同じデータベースレコードが入っているとは限りません。
こうした問題を避けるには、クエリセットを保存して再利用してください:
queryset = Poll.objects.all() print [p.headline for p in queryset] # クエリセットを評価します。 print [p.pub_date for p in queryset] # キャッシュの値を再利用します。
オブジェクトの比較
二つのモデルオブジェクトを比較するには、標準の Python 比較演算子、すなわち 二重等号符: == を使います。背後では二つのモデルオブジェクト間の主キー値 が比較されます。
上の Entry の例では、以下の二つの文は等価になります:
some_entry == other_entry some_entry.id == other_entry.id
モデルの主キーが id という名前でなくても問題はありません。どのような名 前であれ、比較は常に主キーを使って行われます。例えば、モデルの主キーのフィー ルド名が name であれば、以下の二つの文は等価になります:
some_obj == other_obj some_obj.name == other_obj.name
Q オブジェクトを使った複雑な照合
filter() などで複数のキーワード引数を指定してクエリを行うと、各々のキー ワード引数の表す照合条件は違いに "AND" で結ばれます。より複雑なクエリ (例え ば OR を使ったクエリ) を実行する必要がある場合には Q オブジェクトを 使えます。
Q オブジェクト (django.db.models.query.Q) は、複数のキーワード引数 をカプセル化するために使われます。キーワード引数は前述の フィールドの照合 で説明したものと同じです。
例えば、以下の Q オブジェクトは単一の LIKE クエリをカプセル化してい ます:
Q(question__startswith='What')
Q オブジェクトは & や | といった演算子で組み合わせられます。演 算子で結ばれた二つの Q オブジェクトは新たな Q オブジェクトになりま す。
例えば、以下の文は二つの question__startswith クエリを "OR" したものを 表す単一の Q オブジェクトになります:
Q(question__startswith='Who') | Q(question__startswith='What')
この Q オブジェクトは以下の WHERE 節と同じになります:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Q オブジェクトを & と | で組み合わせれば、好きなだけ複雑なクエ リ文を作成できます。丸括弧を使ったグルーピングも可能です。
キーワード引数をとる照合関数 (filter(), exclude(), get() など) には、複数の Q を固定引数として (名前なしの引数として) 渡せます。複数の Q オブジェクトを照合関数に渡した場合、それらは互いに "AND" で結ばれます。 例えば:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
は、だいたい以下のような SQL になります:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
照合関数は Q オブジェクトとキーワード引数を混ぜて使えます。照合関数に渡 した全ての引数は (キーワード引数も Q オブジェクトも) 互いに "AND" で結 ばれます。ただし、 Q を指定する場合はどのキーワード引数よりも前に指定せ ねばなりませんたとえば:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who')
は有効なクエリで、前の例と同じになりますが、以下の文:
# INVALID QUERY
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
は無効です。
OR lookups examples page の例も参照してください。
リレーション
モデル内でリレーション (ForeignKey, OneToOneField, ManyToManyField) を定義すると、そのモデルのインスタンスはリレーション先 のオブジェクトにアクセスするための便利な API を持つようになります。
このドキュメントの冒頭のモデルを例にとると、 Entry オブジェクト e は、 e に関連づけられている Blog オブジェクトに blog という属性 を使って e.blog のようにアクセスできます。
(舞台裏では、この機能は Python の デスクリプタ を使って実装されています。 だからどうだというわけではありませんが、興味のある人のためにここで指摘して おきます。)
Django はまた、リレーションの「相手側」へのアクセス API、すなわちリレーショ ンを張られた側からリレーションを張った側のモデルへのリンクも作成します。例 えば、 Blog オブジェクト b は、リレーションを張った全ての Entry オブジェクトのリストに entry_set 属性を使って b.entry_set.all() の ようにアクセスできます。
この節での例は、全て冒頭に示した Blog Blog, Author, Entry のモデルを使っています。
一対多のリレーション
順方向
モデルに ForeignKey フィールドがある場合、そのモデルのインスタンスは、 単に属性を使ってリレーション先 (外部) のオブジェクトを参照できます。
例:
e = Entry.objects.get(id=2) e.blog # リレーション先の Blog オブジェクトを返します。
外部キー属性の値は取得 (get) も設定 (set) もできます。当然ながら、外部キー への変更は save() を呼び出すまでデータベースに反映されません。
例:
e = Entry.objects.get(id=2) e.blog = some_blog e.save()
ForeignKey フィールドに null=True が設定されていた場合 (NULL 値 を許している場合)、以下の例のように None を代入できます:
e = Entry.objects.get(id=2) e.blog = None e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
一対多のリレーションにおける順方向のアクセスは、リレーション先のオブジェク トに最初にアクセスした際にキャッシュされます。それ以降のアクセスでは、同じ オブジェクトインスタンスの外部キーへのアクセスはキャッシュされた値を返しま す。例えば:
e = Entry.objects.get(id=2) print e.blog # データベースを検索して、関連づけられた Blog を返します。 print e.blog # データベースは検索せず、キャッシュを使います。
クエリセットのメソッド select_related() を使うと、一対多のリレーション のリレーション先オブジェクト全てをあらかじめ再帰的にキャッシュに取り込みま す。例えば:
e = Entry.objects.select_related().get(id=2) print e.blog # Doesn't hit the database; uses cached version. print e.blog # Doesn't hit the database; uses cached version.
select_related() のドキュメントは、前述の 「 新たなクエリセットを返すクエリセットメソッド 」の節にあります。
逆方向
あるモデルが ForeignKey で別のモデルにリレーションを張っている場合、リ レーションを張られた側のモデルのインスタンスは、リレーションを張った側のモ デルの全てのインスタンスを返すマネジャにアクセスできるようになります。リレー ションを張っている側のモデル名を全て小文字にしたものを FOO とすると、マ ネジャの名前のデフォルト値は FOO_set になります。このマネジャはクエリセッ トを返します。クエリセットには前述の「オブジェクトの取得」の節で説明したフィ ルタや操作を行えます。
例:
b = Blog.objects.get(id=1) b.entry_set.all() # Blog に関連づけられた全ての Entry を返します。 # b.entry_set はクエリセットを返すマネジャです。 b.entry_set.filter(headline__contains='Lennon') b.entry_set.count()
ForeignKey を定義するときに related_name パラメタを設定しておくと、 FOO_set の名前をオーバライドできます。例えば、 Entry モデルの定義を blog = ForeignKey(Blog, related_name='entries') のように改めると、上の コード例は以下のようになります:
b = Blog.objects.get(id=1) b.entries.all() # Blog に関連づけられた全ての Entry を返します。 # b.entries はクエリセットを返すマネジャです。 b.entries.filter(headline__contains='Lennon') b.entries.count()
ForeignKey のマネジャへの逆のアクセスはできません。 ForeignKey はイ ンスタンスとしてアクセスせねばなりません。例えば:
# AttributeError: "Manager must be accessed via instance" を送出します。 Blog.entry_set
前述の「オブジェクトの取得」で説明したクエリセットのメソッドに加えて、 ForeignKey を介したマネジャは以下の追加のメソッドを備えています:
add(obj1, obj2, ...): 指定のモデルオブジェクトをリレーション先オ ブジェクトのセットに加えます。
例:
b = Blog.objects.get(id=1) e = Entry.objects.get(id=234) b.entry_set.add(e) # Associates Entry e with Blog b.create(**kwargs): 新たなオブジェクトを作成し、保存して、リレーショ ン先オブジェクトのセットに加えます。作成したオブジェクトを返します。
例:
b = Blog.objects.get(id=1) e = b.entry_set.create(headline='Hello', body_text='Hi', pub_date=datetime.date(2005, 1, 1)) # 自動的に save() されるので、 e.save() を呼ぶ必要はありません。これは以下の操作と同じです (ただし前者の方が簡潔です):
b = Blog.objects.get(id=1) e = Entry(blog=b, headline='Hello', body_text='Hi', pub_date=datetime.date(2005, 1, 1)) e.save()モデルからオブジェクトを作成するときに、リレーションを定義するための キーワード引数を必要としないことに注意して下さい。上の例では、 create() に blog パラメタを渡していません。 Django は Entry オブジェクトの blog フィールドの値が b であることを 自分で判別します。
remove(obj1, obj2, ...): リレーション先オブジェクトのセットから、 指定のオブジェクトを除去します。
例:
b = Blog.objects.get(id=1) e = Entry.objects.get(id=234) b.entry_set.remove(e) # Blog b から Entry e を除去します。データベースの一貫性を壊さないようにするため、このメソッドは null=True である ForeignKey オブジェクトでしか使えません。リ レーション先のフィールドを None (NULL) にできない場合、あるオ ブジェクトを削除しようとしたときに、必ず何らかの別のオブジェクトに追 加せねばならなくなるからです。上の例では、 b.entry_set() から e を除去する操作は e.blog = None と同じになり、 外部キー blog が null=True でないために無効な操作になります。
clear(): リレーション先オブジェクトのセットから全てのオブジェクト を除去します。
例:
b = Blog.objects.get(id=1) b.entry_set.clear()リレーション先のオブジェクト全てを削除するわけではありません。単にリ レーションを解除するだけです。
remove() や clear() と同様、 null=True であるような外部キー でしか使えません。
リレーション先セットのメンバを一括で代入するには、イテレーション可能オブジェ クトを代入します。例えば:
b = Blog.objects.get(id=1) b.entry_set = [e1, e2]
clear() メソッドを利用できる場合、イテレーション可能オブジェクト (上の 例ではリスト) からオブジェクトを entry_set に追加する前に、代入先のセッ トに既に存在するオブジェクトを全て除去します。 clear() メソッドを利用 できない 場合、既に存在するオブジェクトを削除せず、単にイテレーション可能 オブジェクト上の全てのオブジェクトを追加します。
この節で説明した「逆方向の」操作は、いずれもデータベースを即時変更します。 操作結果は追加、新規作成、削除といった操作を行う度に即座に自動的にデータベー スに保存されます。
多対多のリレーション
多対多のリレーションの場合、リレーションの関係にあるモデルの一方は、互いに もう一方にアクセスするための自動 API を獲得します。この API は一対多のリレー ションにおける「逆方向の」参照のように動作します。前述の 逆方向 を参照し てください。
一対多のリレーションとの唯一の違いは、属性の名前づけ規則です。 ManyToManyField を定義した側のモデルはフィールド名をそのまま使いますが、 「反対側の」モデルでは、相手のモデルのモデル名を小文字にして、 '_set' を追加したもの (一対多のリレーションにおける逆方向の参照と同じ) になります。
例を使って説明した方が理解しやすいでしょう:
e = Entry.objects.get(id=3) e.authors.all() # Entry の全ての Author オブジェクトを返します。 e.authors.count() e.authors.filter(name__contains='John') a = Author.objects.get(id=5) a.entry_set.all() # Author の全ての Entry オブジェクトを返します。
ForeignKey と同様、 ManyToManyField には related_name パラメタ を指定できます。 上の例で、 Entry の ManyToManyField に related_name='entries' を指定していた場合、 Author インスタンスは entry_set ではなく entries という属性を持つようになります。
一対一のリレーション
一対一のリレーションのセマンティクスはまもなく変更されるので、使わないよう に勧めます。
リレーションの後方参照はどうやって実現されているのか
他のオブジェクトリレーショナルマッパでは、双方向からリレーションを定義せね ばなりません。 Django の開発者たちはこれを DRY 則 (Don't Repeat Yourself) の侵犯だと考えたため、 Django では片方だけでリレーションを定義すればよいよ うにしています。
しかし、なぜあるモデルクラスが、自分に対してリレーションを張っているモデル クラスのことを、そのクラスがロードされる前に検知できるのでしょうか?
答えは INSTALLED_APPS 設定にあります。最初にモデルをロードした時に、 Django は INSTALLED_APPS の全てのモデルを走査して、必要に応じて後方参照 をメモリ中に作成します。 INSTALLED_APPS の本質的な機能の一つは、 Django にモデルドメインの全体像を知らせることなのです。
リレーション先オブジェクトを使ったクエリ
リレーション先のオブジェクトを照合条件に含むクエリは、通常の値のフィールド の入ったくえりと同じような規則に従います。クエリにマッチ条件として値を指定 する場合、オブジェクトのインスタンス自体か、オブジェクトの主キー値のいずれ かを使えます。
例えば、 id=5 であるようなBlog オブジェクト b に対しては、以下の三 つのクエリはすべて等価になります:
Entry.objects.filter(blog=b) # オブジェクトインスタンスを使ったクエリ Entry.objects.filter(blog=b.id) # インスタンスの id を使ったクエリ Entry.objects.filter(blog=5) # id を直接使ったクエリ
オブジェクトの削除
削除用のメソッドには、便宜的に delete() という名前が付いてます。このメ ソッドはオブジェクトをただちに削除し、戻り値を返しません。例えば:
e.delete()
複数のオブジェクトの一斉削除も可能です。 QuerySet には delete() メソッドがあり、 QuerySet の全てのメンバを削除します。
例えば、 pub_date が 2005 年の Entry オブジェクトを全て削除するには 以下のようにします:
Entry.objects.filter(pub_date__year=2005).delete()
Django は、オブジェクトを削除する際に、SQLでいう ON DELETE CASCADE 制約 をエミュレートします。すなわち、削除対象のオブジェクトを指すような外部キー を持つ全てのオブジェクトも同時に削除されるのです。:
b = Blog.objects.get(pk=1) # 次の命令は、 Blog と Blog を指す Entry 全てを削除してしまいます。 b.delete()
delete() は QuerySet のメソッドにすぎず、 Manager 自体には公開 されていないので注意してください。これは誤って Entry.objects.delete() を実行して 全ての エントリを削除してしまわないようにするための安全機構で す。本当に全てのオブジェクトを削除 したい のなら、以下のように明示的に全 てのオブジェクトを表すクエリセットをリクエストしてください:
Entry.objects.all().delete()
追加のインスタンスメソッド
save(), delete() に加えて、モデルオブジェクトは以下のいずれか、あるい は全てのメソッドを持つことがあります:
get_FOO_display()
choices セットを持つ全てのフィールドについて、オブジェクトは get_FOO_display() メソッドを持ちます。 FOO はフィールド名です。この メソッドは、「人間可読な」フィールド名を返します。例えば、以下のモデル:
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
class Person(models.Model):
name = models.CharField(max_length=20)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
では、各 Person インスタンスは get_gender_display() メソッド を持ちます:
>>> p = Person(name='John', gender='M') >>> p.save() >>> p.gender 'M' >>> p.get_gender_display() 'Male'
get_next_by_FOO(**kwargs) and get_previous_by_FOO(**kwargs)
null=True であるような DateField および DateTimeField フィール ドについて、オブジェクトは get_next_by_FOO() および get_previous_by_FOO() メソッドを持ちます。 FOO はフィールド名です。 このメソッドは該当の日付フィールドに応じて前のオブジェクトや次のオブジェク トを返します。適切なオブジェクトがなければ DoesNotExist を送出します。
これらのメソッドはいずれもオプションのキーワード引数をとります。引数は 前述の「フィールド検索」で解説した形式にします。
同じ日付値を持つオブジェクトがある場合、このメソッドは ID を使ったチェック にフォールバックします。これにより、レコードがスキップしたり重複したりしな いことが保証されています。完全なコードの例は 照合 API のサンプルモデル を参照してください。
get_FOO_filename()
各 FileField につき、オブジェクトは get_FOO_filename() メソッドを持 ちます。 FOO はフィールド名です。このメソッドは、 MEDIA_ROOT の設定 に基づいてファイルのファイルシステム上での完全なパスを返します。
ImageField は技術的に FileField のサブクラスであるため、 ImageField のフィールドを持つ全てのモデルはこのメソッドを使えます。
get_FOO_url()
各 FileField につき、オブジェクトは get_FOO_url() メソッドを持ちま す。 FOO はフィールド名です。このメソッドは、 MEDIA_URL の設定に基づ いてファイルの完全な URL を返します。フィールドの値が空の場合、空文字列を返 します。
get_FOO_size()
各 FileField につき、オブジェクトは get_FOO_filename() メソッドを持 ちます。 FOO はフィールド名です。このフィールドはファイルのサイズをバイ トを単位として返します (背後では os.path.getsize を使っています)。
save_FOO_file(filename, raw_contents)
各 FileField につき、オブジェクトは get_FOO_filename() メソ