.. _topics-db-queries: ================= クエリを生成する ================= :revision-up-to: 17812 (1.4) .. currentmodule:: django.db.models :doc:`データモデル ` を作成したら、次はデータベースからデー タを取り出す必要があります。このドキュメントでは、モデルから利用できるデー タベース抽象化 API と、オブジェクトを生成、取得、更新する方法について説明し ます。モデルの照合オプションの詳細は :doc:`データモデルリファレンス ` を参照してください。 このリファレンスでは、以下のような Poll アプリケーションを参考に話を進めま す: .. _queryset-model-example: .. code-block:: python 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() mod_date = models.DateTimeField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __unicode__(self): return self.headline オブジェクトの生成 ================== Django では、データベーステーブル上のデータを Python オブジェクトで表現する ために、モデルクラスがデータベーステーブルを表現し、クラスのインスタンスが テーブル上のレコードを表現するという直感的なシステムを使っています。 オブジェクトを生成するには、キーワード引数を使ってモデルクラスのインスタン スを生成し、 :meth:`~django.db.models.Model.save` メソッドを呼び出して データベースに保存します。 モデルクラスは Python パス上のどこからでも import でき、期待通りに動作しま す (わざわざこのような説明をするのは、以前のバージョンの Django ではモデル の import 方法がかなり風変わりだったからです)。 モデルが ``mysite/blog/models.py`` というファイルで定義されているとすると、 オブジェクトの作成は以下の例のようになります:: >>> from blog.models import Blog >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save() この操作によって、背後では ``INSERT`` SQL 文が実行されます。 Django はユー ザが明示的に :meth:`~django.db.models.Model.save` を呼び出すまで データベースを操作しません。 :meth:`~django.db.models.Model.save` メソッドには戻り値がありません。 .. seealso:: :meth:`~django.db.models.Model.save` には高度な使い方のための オプションがありますが、ここでは解説しません。詳しくは :meth:`~django.db.models.Model.save` のドキュメントを参照してください。 ワンステップでオブジェクトを生成して保存するには :meth:`~django.db.models.query.QuerySet.create()` メソッドを使って ください。 オブジェクトへの変更を保存する ============================== すでにデータベース上にあるオブジェクトへの変更を保存するには :meth:`~django.db.models.Model.save` を使います。 ``Blog`` インスタンス ``b5`` がすでにデータベース上にあるとすると、以下の例 は ``b5`` の名前を変更して、データベース上のレコードを更新します:: >> b5.name = 'New name' >> b5.save() この例では、背後で ``UPDATE`` SQL 文が実行されています。 Django は明示的に :meth:`~django.db.models.Model.save` を呼び出すまでデータベースを操作しません。 ``ForeignKey`` や ``ManyToManyField`` の保存 --------------------------------------------- :class:`~django.db.models.ForeignKey` フィールドの更新は、 通常のフィールドへの変更と同じです。すなわち、 適切な型のオブジェクトを代入して保存すると、フィールドの値を更新でき ます。この例では、``Entry`` のインスタンス ``entry``の ``blog`` アトリビュートを更新しています。:: >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") >>> entry.blog = cheese_blog >>> entry.save() :class:`~django.db.models.ManyToManyField` の更新は少し違います。 リレーションにレコードを一つ追加したい場合は :meth:`~django.db.models.fields.related.RelatedManager.add` メソッドを使いま す。この例では、 ``entry`` オブジレェクトに、 ``Author`` インスタンスの ``joe`` を加えています:: >>> from blog.models import Author >>> joe = Author.objects.create(name="Joe") >>> entry.authors.add(joe) 複数のレコードを :class:`~django.db.models.ManyToManyField` に一度で加えたい 場合、 :meth:`~django.db.models.fields.related.RelatedManager.add` に、 複数の引数をくるんで呼び出せばいいのです。:: >>> john = Author.objects.create(name="John") >>> paul = Author.objects.create(name="Paul") >>> george = Author.objects.create(name="George") >>> ringo = Author.objects.create(name="Ringo") >>> entry.authors.add(john, paul, george, ringo) 間違った型のオブジェクトを外部キーに代入したり ``add()`` したりすると Django はエラーを出します。 オブジェクトの取得 ================== オブジェクトをデータベースから取得するには、モデルクラスのマネジャ :class:`~django.db.models.Manager` を介してクエリセット :class:`~django.db.models.query.QuerySet` を構築します。 :class:`~django.db.models.query.QuerySet` はデータベース上にある オブジェクトの集まりを表現しています。 :class:`~django.db.models.query.QuerySet`には、集合を指定パラメタに従って 絞り込むための条件である*フィルタ (filter)* を複数個持たせられます。 SQL 用語でいえば、クエリセットは ``SELECT`` 文であり、 フィルタは ``WHERE`` や ``LIMIT`` のような限定節にあたります。 :class:`~django.db.models.query.QuerySet` はモデル の:class:`~django.db.models.Manager` から取得します。モデルには最低一つの :class:`~django.db.models.Manager` があり、デフォルトでは ``objects`` という 名前がついています。マネジャにはモデルクラスから直接アクセスしてください:: >>> Blog.objects >>> b = Blog(name='Foo', tagline='Bar') >>> b.objects Traceback: ... AttributeError: "Manager isn't accessible via Blog instances." .. note:: (「テーブルレベル」の操作と「レコードレベル」の操作を分離させるため、マ ネジャはモデルのインスタンスではなくモデルクラスだけからアクセスできる ようになっています。) モデル内での ``QuerySets`` の主なソースは :class:`~django.db.models.Manager` です。 マネジャは、データベースオブジェクト上の全てのオブジェクトを表す「ルートの」 クエリセットであるかのように振舞います。例えば、初期クエリセットである ``Blog.objects`` には、データベース上の全ての ``Blog`` オブジェクトが入っています。 全てのオブジェクトの取得 ------------------------ テーブルからオブジェクトを取得する最も単純な方法は全てのオブジェクトを取得 するというものです。全オブジェクトを取得するには、 :class:`~django.db.models.Manager` の :meth:`~django.db.models.query.QuerySet.all` メソッドを使って下さい:: >>> all_entries = Entry.objects.all() :meth:`~django.db.models.query.QuerySet.all` メソッドはデータベース上の全ての オブジェクトを表現する :meth:`~django.db.models.query.QuerySet` を返します。 (``Entry.objects`` がクエリセットを返すというのなら、なぜ単に ``Entry.objects`` と書かないのでしょうか?それは、ルートのクエリセットであ る ``Entry.objects`` が特別扱いされていて、値評価できないようになっているか らです。 ``all()`` メソッドは、値評価 *できる* クエリセットを返します。 フィルタを使ってオブジェクトを取り出す ---------------------------------------- :class:`~django.db.models.Manager` によって提供される、 いわゆるルート :class:`~django.db.models.query.QuerySet` を使えば、 データベーステーブル上の全てのオブジェクトを表せます。とはいえ、 通常は全オブジェクトの集合からサブセットだけを取り出したいことでしょう。 サブセットを作成するには、フィルタ条件を追加して、 初期 :class:`~django.db.models.query.QuerySet` を リファインする必要があります。 :class:`~django.db.models.query.QuerySet` の洗練には、主に二つの方法があります: ``filter(**kwargs)`` 指定した照合パラメタに一致するオブジェクトの集合を表現する、新たな :class:`~django.db.models.query.QuerySet` を返します。 ``exclude(**kwargs)`` 指定した照合パラメタに一致 *しない* オブジェクトの集合を表現する、 新たな :class:`~django.db.models.query.QuerySet` を返します。 照合パラメタ (上の関数定義における ``**kwargs``) は、後述の `フィールドの照合`_ で解説するフォーマットにせねばなりません。 例えば、 2006 年のブログエントリを表す :class:`~django.db.models.query.QuerySet` を取得するには、以下のように :meth:`~django.db.models.query.QuerySet.filter` を使います:: Entry.objects.filter(pub_date__year=2006) (``Entry.objects.all().filter(...)`` のように、 :meth:`~django.db.models.query.QuerySet.all` を使わなくてもよいことに注意して下さい。 :meth:`~django.db.models.query.QuerySet.all` を使っても問題なく動作しますが、 :meth:`~django.db.models.query.QuerySet.all` が必要となるのはルートクエリセットから 全てのオブジェクトを取り出したい場合だけです。) .. _chaining-filters: フィルタの連鎖 ~~~~~~~~~~~~~~ :class:`~django.db.models.query.QuerySet` をリファインした結果は、 それ自体 :class:`~django.db.models.query.QuerySet` になります。 従って、リファイン操作は連鎖させられます。例えば:: >>> 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 から今日までの間に公開されたエントリです。 .. _filtered-querysets-are-unique: フィルタしたクエリセットは一意なオブジェクトである ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :class:`~django.db.models.query.QuerySet` のリファインを行うと、 その都度新たなクエリセットを得ます。新たな :class:`~django.db.models.query.QuerySet` は 以前のクエリセットになんら縛られていません。リファイン操作のたびに、 別個の独立した :class:`~django.db.models.query.QuerySet` が作成され、個別に保存したり、 再利用したりできます。 例えば:: >> 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``) に影響を及ぼし ません。 .. _querysets-are-lazy: クエリセットは遅延評価される ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ クエリセットの評価は遅延型 (lazy) です。すなわち、 :class:`~django.db.models.query.QuerySet` の作成自体は データベース操作を引き起こしません。 :class:`~django.db.models.query.QuerySet` は *評価される* までデータ ベースへのクエリを実行しないので、延々フィルタを重ねられます:: >>> q = Entry.objects.filter(headline__startswith="What") >>> q = q.filter(pub_date__lte=datetime.now()) >>> q = q.exclude(body_text__icontains="food") >>> print q この例では、データベースに 3 度アクセスするように見えますが、実際には一度だ け、最後の行のとき (``print q``) しかアクセスしません。一般に、 ``QuerySet`` の結果は、その内容を「調べる」まで、データベースから取り出され ません。 ``QuerySet`` は、クエリの内容を調べたときにはじめて *値評価* され るのです。値評価がいつ行われているかは、 :ref:`when-querysets-are-evaluated` で詳しく説明しています。 .. _retriving-single-object-with-get: ゲットを使って一つのオブジェクトを取り出す ------------------------------------------ :meth:`~django.db.models.query.QuerySet.filter` は常に、 :class:`~django.db.models.query.QuerySet` を渡し、もし一つだけ オブジェクトがマッチするクエリ - のようなケースならば、 これは単一の要素を含んだ :class:`~django.db.models.query.QuerySet` です。 もし、クエリにマッチするのが一つだけだと分かるなら、 `Manager` の :meth:`~django.db.models.query.QuerySet.get` メソッドが使えます。 これはオブジェクトを直接的に返します。:: >>> one_entry = Entry.objects.get(pk=1) :meth:`~django.db.models.query.QuerySet.filter` と同じように、 クエリは :meth:`~django.db.models.query.QuerySet.get` と一緒に使えます、 もう一度 `フィールドの照合`_ を見てください。 :meth:`~django.db.models.query.QuerySet.get` を使うのと、 :meth:`~django.db.models.query.QuerySet.filter` を ``[0]`` でスライス するのとでは違いがあります。もし、クエリにマッチするものがなかった時、 :meth:`~django.db.models.query.QuerySet.get` は ``DoesNotExist`` 例外を 送出します。この例外はモデルクラスの属性で、クエリは上にあるコードのように 動きます、もし ``Entry`` オブジェクトに 1 というプライマリーキーがないなら、 Django は ``Entry.DoesNotExist`` 例外を送出します。 同様に、 Django は :meth:`~django.db.models.query.QuerySet.get` クエリで 一つ以上のアイテムとマッチした場合にも例外を送出します。このようなケースでは、 ``MultipleObjectsReturned`` がモデルクラス自身の属性に反しています。 その他のクエリセットメソッド ---------------------------- ほとんどの場合、データベース上のオブジェクトを照合するには、 :meth:`~django.db.models.query.QuerySet.all` か、 :meth:`~django.db.models.query.QuerySet.get` :meth:`~django.db.models.query.QuerySet.filter` :meth:`~django.db.models.query.QuerySet.exclude` メソッドで事足ります。とはいえ、クエリセットがサポートしているメソッドは もっとたくさんあります。クエリセットメソッドの詳細な説明は、 :ref:`クエリセット API リファレンス ` を参照してください。 .. _limiting-querysets: クエリセットに制約を課す ------------------------ クエリセットの返す結果を特定の個数に制限したい場合には、 Python の配列スラ イス表記を使います。これは SQL の ``LIMIT`` 節や ``OFFSET`` 節と等価です。 以下の例は、最初の 5 オブジェクトだけを返します (``LIMIT 5`` に相当します):: >>> Entry.objects.all()[:5] 以下の例は、 6 番目から 10 番目までのオブジェクトを返します (``OFFSET 5 LIMIT 5`` に相当します):: >>> Entry.objects.all()[5:10] 負の数でのインデクシングはサポートしていません。 (すなわち、 ``Entry.objects.all()[-1]`` このようなものです) 一般に、 :class:`~django.db.models.query.QuerySet` をスライスしても :class:`~django.db.models.query.QuerySet` を新たに生成して返すだけで、 クエリの評価は行いません。例外はスライス表記に「ステップ (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`` を送出します。詳しくは :meth:`~django.db.models.query.QuerySet.get` のドキュメントを参照してください。 .. _field-lookups-intro: フィールドの照合 ---------------- フィールドの照合操作によって、 SQL の ``WHERE`` 節の中身が決まります。フィー ルドの照合を行うには、 :meth:`~django.db.models.query.QuerySet.filter`, :meth:`~django.db.models.query.QuerySet.exclude` および :meth:`~django.db.models.query.QuerySet.get` といった :class:`~django.db.models.query.QuerySet` のメソッドのキーワード引数を指定します。 基本的に、照合のキーワード引数名は ``field__lookuptype=value`` のような形 式をとります (アンダースコアは二重です)。例えば:: >>> Entry.objects.filter(pub_date__lte='2006-01-01') は、(大雑把にいって) 以下のような SQL 文に変換されます:: SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01'; .. admonition:: これがどうして可能なのか Python には、任意の名前と値をもった引数を受け取れる関数を定義する機能が あり、引数名とその値は実行時に評価されます。くわしい情報は公式の Python チュートリアルの `キーワード引数`_ を参照してください。 .. _`Keyword Arguments`: http://docs.python.org/tutorial/controlflow.html#keyword-arguments .. _`キーワード引数`: http://www.python.jp/doc/release/tut/node6.html#SECTION006720000000000000000 .. versionchanged:: 1.4 照合の中でのフィールドの指定は、モデルフィールドの名前でなければいけませ ん。 :class:`~django.db.models.ForeignKey` の場合、例外ががありますが、 フィールドネームを ``_id`` というサフィックス(suffix)で指定できます。 このようなケースでは、バリューパラメータはフォーリンモデルの プライマリーキーの生の値を含みます。例えば:: >>> Entry.objects.filter(blog_id__exact=4) 照合時に無効なキーワード引数が渡されると、 ``TypeError`` が送出されます。 データベース API は 2 ダース近くの照合タイプをサポートしています。詳しいリ ファレンスは :ref:`フィールド照合リファレンス ` を参照してく ださい。ここでは、よく使う照合タイプをいくつか示します: :lookup:`exact` 「厳密一致」です。例えば:: >>> Entry.objects.get(headline__exact="Man bites dog") は、以下のような SQL を生成します: .. code-block:: sql SELECT ... WHERE headline = 'Man bits dog'; 照合タイプを指定しなかった場合、つまり二重アンダースコアの入ったキー ワード引数を使わないかぎり、照合条件は ``exact`` とみなされます。 例えば、以下の二つの文は同じ意味です:: >>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied ``exact`` はよく使われるため、便宜的にこのような仕様にしています。 :lookup:`iexact` 大小文字を区別しない一致です。以下のクエリ:: >>> Blog.objects.get(name__iexact="beatles blog") は、 "Beatles Blog", "beatles blog", "BeAtlES blOG" といったタイト ルの ``Blog`` オブジェクトにマッチします。 :lookup:`contains` 大小文字を区別する部分一致テストです。以下のクエリ:: Entry.objects.get(headline__contains='Lennon') は、おおざっぱにいって以下の SQL に変換されます: .. code-block:: sql SELECT ... WHERE headline LIKE '%Lennon%'; ``'Today Lennon honored'`` には一致しますが、 ``'today lennon honored'`` には一致しないので注意してください。 大小文字を区別しない :lookup:`icontains` もあります。 :lookup:`startswith`, :lookup:`endswith` それぞれ、前方一致と後方一致です。大小文字を区別しない :lookup:`istartswith` や :lookup:`iendswith` もあります。 ここに挙げたのはほんの一部です。全ての照合タイプの解説は :ref:`フィールド照合タイプのリファレンス ` を参照してくださ い。 .. _lookups-that-span-relationships: リレーションをまたいだ照合 --------------------------- 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') 複数のリレーションをまたいでフィルタを行っていて、中間のモデルにフィルタ条 件に合致する値が存在しないと、 Django はその値が空である (``NULL`` が入って いる) だけの有意なオブジェクトとして扱います。つまり、エラーを送出しません。 例えば、以下のフィルタを考えましょう:: Blog.objects.filter(entry__author__name='Lennon') (``Author`` モデルは存在するものとします) ``entry`` の ``author`` を空にし ていると、 Django は ``author`` が存在しないために ``name`` を辿れませんが、 エラーは送出せず、あたかも ``name`` も空であるかのように扱います。たいてい は、この仕様で期待通りに動きますが、 :lookup:`isnull` を使うと、少し混乱をきたし ます。すなわち:: Blog.objects.filter(entry__author__name__isnull=True) は、 ``author`` の ``name`` が空の ``Blog`` だけでなく、 ``entry`` の ``author`` が空の ``Blog`` も返してしまうのです。後者のオブジェクトを返して 欲しくないのなら、以下のように書いてください:: Blog.objects.filter(entry__author__isnull=False, entry__author__name__isnull=True) 多値のリレーションをスパンする ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :class:`~django.db.models.ManyToManyField` や :class:`~django.db.models.ForeignKey` の逆リレーションを使ってフィルタを行 う場合、 2 種類の異なるフィルタ方法があります。 ``Blog`` / ``Entry`` のリレー ション (``Blog`` から ``Entry`` が 1 対多であるようなリレーション) を考えて みましょう。このようなモデルでは、例えばヘッドラインに *'Lennon'* を含み、 **かつ** 2008 年に公開されたエントリを持つようなブログを検索したい場合があ るでしょう。あるいは、ヘッドラインに *'Lennon'* を含むか、 **あるいは** 2008 年に公開されたエントリを含むようなブログの検索もあり得ます。複数のエン トリが一つの ``Blog`` に関連付けられているので、いずれのクエリも実行可能で すし、意味をなす場合があるでしょう。 :class:`~django.db.models.ManyToManyField` を使っている場合にも、 同じような状況が起こります。例えば、 ``Entry`` に ``tags`` という名前の :class:`~django.db.models.ManyToManyField` があり、 *"music"* や *"band"* というタグに関連付けられたエントリを検索したい場合や、 *"music"* タグのついた *"public"* 状態のエントリを取り出したい場合があるで しょう。 こうした状況をうまく扱うために、 Django には :meth:`~django.db.models.query.QuerySet.filter` および :meth:`~django.db.models.query.QuerySet.exclude` といった呼び出しがあります。 :meth:`~django.db.models.query.QuerySet.filter` に指定した条件は同時に適用され、 条件全てに一致する要素をフィルタします。 ``filter()`` を連続 して呼び出すと、通常はオブジェクトの集合により狭い制約をかけますが、多値リ レーションの場合、新たな制約は、前の ``filter()`` で絞り込んだリレーション 先をさらに絞り込むのではなく、前の ``filter()`` で絞り込んだリレーション先 にリンクしているリレーション元からリレーションを張られている全てのオブジェ クトに対して適用されてしまいます。 この話はちょっとややこしいので、例を挙げて解説しましょう。 *'Lennon'* をヘッ ドラインに含み、 2008 年に公開されたエントリを含むブログを選択するには、以 下のように書きます:: Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008) 一方、 *'Lennon'* をヘッドラインに含むか、 **あるいは** 2008 年に公開された エンリを含むブログを検索するには、以下のように書きます:: Blog.objects.filter(entry__headline__contains='Lennon').filter( entry__pub_date__year=2008) 後者の例では、最初の ``filter()`` でクエリセットに制約をかけ、特定のエント リにリンクを持つブログだけを取り出しています。次の ``filter()`` では、取り 出した ``Blog`` オブジェクトから、 *さらに* 二つ目の条件に合うものに制約を かけています。従って、二つ目の ``filter()`` で制約をかけている対象の ``entry`` は、最初のフィルタで絞り込んだエントリと同じときもあれば、違うと きもあります。一つめの ``filter()`` でフィルタしているのはあくまでも ``Blog`` であって、 ``Entry`` ではないからです。 この挙動は、 :meth:`~django.db.models.query.QuerySet.exclude` にもあてはまります。 一つの :meth:`~django.db.models.query.QuerySet.exclude` に指定した条件は、 (多値リレーションの場合は) 各インスタンスに同時に適用されます。 :meth:`~django.db.models.query.QuerySet.filter` や :meth:`~django.db.models.query.QuerySet.exclude` を連続して呼び出すと、 毎回違うリンク先集合を使ってフィルタを行ってしまう可能性があるのです。 .. _query-expressions: フィルタはモデルのフィールドを参照できる ---------------------------------------- これまで与えられた例の中で、私たちはフィルタを同じモデルフィールドの バリューと比較してきました。しかし、モデルの値を同一のモデルの別のフィールドと 比較したい場合はどうしたらよいのでしょう? Django は :ref:`F() expressions ` をこの比較のために 提供しています。 ``F()`` のインスタンスはクエリの内側のモデルフィールドへの 参照として働きます。これらのは同じモデルインス タンスの異なる二つのフィールドを比較するクエリフィルターです。 例えば、これは pingbacks よりコメントが多い全てのブログエントリーのリスト を見つけるものです。 ``F()`` オブジェクトを使って、 pingback のカウント数 が参照できるようにすます。:: >>> from django.db.models import F >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks')) Django は追加や削除、かけ算、分割と剰余などの計算も ``F()`` オブジェクトで 使えるようにサポートしています。ブログエントリーの中で、 pingbacks よりコメン トが *二倍* 以上あるものを抽出するには、クエリをこのようにします:: >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2) ブログエントリーの中で、 pingback 自体ののカウント数とコメント数よりも ブログエントリーの、エントリーのレーティングが小さい・少ないものにするには このようにクエリを書きます。:: >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) また、 ``F()`` オブジェクト内で離れた関係を表記するために、ダブルアンダー スコア表記を用いることができます。ダブルアンダースコア付きの ``F()`` オブジェクトは、ひもづいているオブジェクトへのアクセスが必要な 時のために存在しています。例えば、作者の名前がブログの名前と同じ 全てのエントリーを取り出そうとしたとき、このようなクエリが発行できます。 >>> Entry.objects.filter(authors__name=F('blog__name')) .. versionadded:: 1.3 date と date/time フィールドは、 :class:`~datetime.timedelta` オブジェクト で足したり引いたりできます。これは発行されてから三日以上経た全ての エントリーを抽出するクエリです:: >>> from datetime import timedelta >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) 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 を表す LIKE 文におけるパーセント記号とアンダースコアのエスケープ ----------------------------------------------------------- ``LIKE`` を使う SQL 文になるようなフィールド照合メソッド (``iexact``, ``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith``, ``iendswith``) では、 ``LIKE`` 文で使われる二つの特殊な文字、すなわちパーセ ント記号とアンダースコアを自動的にエスケープします。 (``LIKE`` 文では、パー セント記号は任意の複数文字に対するワイルドカードを表し、アンダースコアは任 意の一文字に対するワイルドカードを表します。) この機能によって、照合操作を直感的に行え、データベースの抽象化を守れます。 例えば、パーセント記号を含むようなエントリ全てを取得したければ、以下のよう にパーセント記号をそのまま使います:: >>> Entry.objects.filter(headline__contains='%') Django はクオートの処理に気を配ってくれます。 SQL は以下のような感じになり ます: .. code-block:: sql SELECT ... WHERE headline LIKE '%\%%'; アンダースコアについても同じようなエスケープを行います。パーセント記号とア ンダースコアはいずれも透過的に処理されます。 .. _caching-and-querysets: キャッシュとクエリセット ------------------------ データベースへのアクセスを最小限にとどめるため、 :class:`~django.db.models.query.QuerySet` 各々にはキャッシュがあります。 効率的なコードを書く上で、キャッシュのからくりを理解しておくのは重要なことです。 :class:`~django.db.models.query.QuerySet` が新たに生成された時点では、 キャッシュは空です。 :class:`~django.db.models.query.QuerySet` を 最初に評価したとき (すなわち、データベースへのクエリが最初に生じたとき)、 Django はクエリ結果をクエリセットオブジェクト内のキャッシュに保存し、明示的 にリクエストした結果だけ (例えば、 :class:`~django.db.models.query.QuerySet`\'s に対してイテレーション操作を する場合には、結果セットの最初の要素) を返します。それ以後は、 :class:`~django.db.models.query.QuerySet`\' を再利用するとキャッシュ済みの結果を返します。 このキャッシュの挙動をよく覚えておいて下さい。というのも、 :class:`~django.db.models.query.QuerySet` を正しく扱わないと、 おもわぬところで手を噛まれるはめになるからです。例えば、以下の例では 二つの :class:`~django.db.models.query.QuerySet` を作成し、値を評価して、 すぐにクエリセットを捨ててしまっています:: >>> print [e.headline for e in Entry.objects.all()] >>> print [e.pub_date for e in Entry.objects.all()] そのため、全く同じデータベースクエリを二度実行し、データベースの負荷を倍加 させています。また、 ``Entry`` は二つのリクエストを処理する間にも追加された り削除されたりする可能性があるため、二つのリストには必ずしも同じデータベー スレコードが入っているとは限りません。 こうした問題を避けるには、 :class:`~django.db.models.query.QuerySet` を保存して再利用してください:: >>> queryset = Entry.objects.all() >>> print [p.headline for p in queryset] # クエリセットを評価します。 >>> print [p.pub_date for p in queryset] # キャッシュの値を再利用します。 Q オブジェクトを使った複雑な照合 ================================ :meth:`~django.db.models.query.QuerySet.filter` などで複数の キーワード引数を指定してクエリを行うと、各々のキーワード引数の表す照合条件は 違いに "AND" で結ばれます。より複雑なクエリ (例えば ``OR`` を使ったクエリ) を 実行する必要がある場合には ``Q`` オブジェクトを使えます。 .. comment: Link to Q does not work, since this documentation does not exist yet. :class:`~django.db.models.Q` オブジェクト (``django.db.models.Q``) は、 複数のキーワード引数をカプセル化するために使われます。 キーワード引数は前述の 「フィールドの照合」で説明したものと同じです。 例えば、以下の ``Q`` オブジェクトは単一の ``LIKE`` クエリをカプセル化してい ます:: from django.db.models import Q 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`` オブジェクトを ``&`` と ``|`` で組み合わせれば、好きなだけ複雑なクエ リ文を作成できます。丸括弧を使ったグルーピングも可能です。また、 ``~`` 演算 子を使えば ``Q`` オブジェクトの「否 (nagate)」を取れるので、例えば以下のよ うに、通常のクエリと否のクエリの積をとれます:: Q(question__startswith='Who') | ~Q(pub_date__year=2005) キーワード引数をとる照合関数 ( :meth:`~django.db.models.query.QuerySet.filter` :meth:`~django.db.models.query.QuerySet.exclude` :meth:`~django.db.models.query.QuerySet.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))) は無効です。 .. seealso:: `OR lookups examples`_ の例には、 ``Q`` を使った Django のユニット テストがあります。 .. _OR lookups examples: https://code.djangoproject.com/browser/django/trunk/tests/modeltests/or_lookups/tests.py オブジェクトの比較 ================== 二つのモデルオブジェクトを比較するには、標準の Python 比較演算子、すなわち 二重等号符: ``==`` を使います。背後では二つのモデルオブジェクト間の主キー値 が比較されます。 上の ``Entry`` の例では、以下の二つの文は等価です:: >>> some_entry == other_entry >>> some_entry.id == other_entry.id モデルの主キーが ``id`` という名前でなくても問題はありません。どのような名 前であれ、比較は常に主キーを使って行われます。例えば、モデルの主キーのフィー ルド名が ``name`` であれば、以下の二つの文は等価です:: >>> some_obj == other_obj >>> some_obj.name == other_obj.name .. _topics-db-queries-delete: オブジェクトの削除 ================== 削除用のメソッドには、 便宜的に :meth:`~django.db.models.Model.delete` という名前が付いてます。 このメソッドはオブジェクトをただちに削除し、戻り値を返しません:: e.delete() 複数のオブジェクトの一斉削除も可能です。 :class:`~django.db.models.query.QuerySet` には :meth:`~django.db.models.query.QuerySet.delete`メソッドがあり、 :class:`~django.db.models.query.QuerySet` の全てのメンバを削除します。 例えば、 ``pub_date`` が 2005 年の ``Entry`` オブジェクトを全て削除するには 以下のようにします:: Entry.objects.filter(pub_date__year=2005).delete() 忘れてならないのは、この処理はできる限り SQL レベルで行われるため、 ``delete()`` メソッドが全てのインスタンスの ``delete()`` メソッドを呼ぶとは 限らないということです。モデルクラス上で ``delete()`` をカスタマイズしてい て、オブジェクトを削除するときに呼び出したいのなら、 :class:`~django.db.models.query.QuerySet` の ``delete()`` メソッドで一括削除するのではなく、直接 ( :class:`~django.db.models.query.QuerySet` の各要素を 取り出して逐次 ``delete()`` を呼ぶなどして) 「手動で」インスタンスを削除せ ねばなりません。 Django は、オブジェクトを削除する際に、通常 SQLでいう ``ON DELETE CASCADE`` 制約をエミュレートします。すなわち、 削除対象のオブジェクトを指すような外部キーを持つ全てのオブジェクトも 同時に削除されるのです。:: b = Blog.objects.get(pk=1) # 次の命令は、 Blog と Blog を指す Entry 全てを削除してしまいます。 b.delete() .. versionadded:: 1.3 このカスケードの挙動は :class:`~django.db.models.ForeignKey` の :attr:`~django.db.models.ForeignKey.on_delete` 引数を通して変えることが出 来ます。 :meth:`~django.db.models.query.QuerySet.delete` は :class:`~django.db.models.query.QuerySet` のメソッドにすぎず、 :class:`~django.db.models.Manager` 自体には公開 されていないので注意してください。これは誤って ``Entry.objects.delete()`` を実行して *全ての* エントリを削除してしまわないようにするための安全機構で す。本当に全てのオブジェクトを削除 *したい* のなら、以下のように明示的に全 てのオブジェクトを表すクエリセットをリクエストしてください:: Entry.objects.all().delete() .. _topics-db-queries-copy: モデルインスタンスをコピーする ============================== モデルインスタンスをコピーする組み込みのメソッドは提供されていません、 ですが全てのフィールドとバリューを同一にした新しいインスタンスを簡単に作成する ことができます。最もシンプルなケースでは、 ``pk`` を ``None`` にすることができ ます。ブログの例を使うと:: blog = Blog(name='My blog', tagline='Blogging is easy') blog.save() # post.pk == 1 blog.pk = None blog.save() # post.pk == 2 継承を使うと、もう少し複雑になってしまいます。 ``Blog`` のサブクラスについて 考えてみましょう。:: class ThemeBlog(Blog): theme = models.CharField(max_length=200) django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme = 'python') django_blog.save() # django_blog.pk == 3 継承の働きによって、 ``pk`` と ``id`` を None にしなくてはいけません:: django_blog.pk = None django_blog.id = None django_blog.save() # django_blog.pk == 4 このプロセスはひもづいているオブジェクトをコピーしません。もし、関係性のあるも のもコピーしたいのならば、もう少し多めにコードを書かなければいけません。 ``Entry`` が ``Author`` に many to many フィールドを持っている場合:: entry = Entry.objects.all()[0] # some previous entry old_authors = entry.authors.all() entry.pk = None entry.save() entry.authors = old_authors # saves new many2many relations .. _topics-db-queries-update: 複数のオブジェクトを一度に更新する =================================== :class:`~django.db.models.query.QuerySet` が表現する全てのオブジェクトのある フィールドに特定の値を指定したいような場合があります。 :meth:`~django.db.models.query.QuerySet.update` メソッドを使えば、 この処理を実現できます。例えば:: # pub_date が 2007 の全エントリのヘッドラインを更新する Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') この操作を行えるのは、リレーションフィールドでないフィールドか、 :class:`~django.db.models.ForeignKey` のフィールドのみです。また、フィールドに指定する値はハードコー ドされた Python の値でなければなりません (つまり、その時の何らかのフィール ド値は直接セットできません)。 :class:`~django.db.models.ForeignKey` のフィールドを更新するには、以下のように、 新たなモデルインスタンスを作成して渡します:: >>> b = Blog.objects.get(pk=1) # 全てのエントリをこの blog に属させる >>> Entry.objects.all().update(blog=b) ``update()`` メソッドは (``delete()`` と同様)、即座に適用され、値を返しませ ん。また、更新対象のクエリセットには、モデルの主のテーブル一つに対してし かアクセスできないという制限があります。フィルターは関係するテーブルに 使えますが、アップデートできるのはモデルのメインテーブルのカラムだけです。:: >>> b = Blog.objects.get(pk=1) # この blog に属している全ての headline をアップデートします。 >>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same') ``update()`` メソッドは SQL 文に直接変換されるので注意してください。このメ ソッドは、フィールド値の直接更新を大量一括実行するためのものです。 モデルの :meth:`~django.db.models.Model.save` メソッドを呼出さないので、 ( :class:`~django.db.models.query.QuerySet` の呼び出しに連動している) ``pre_save`` や ``post_save`` といった シグナルは発生しません。:class:`~django.db.models.query.QuerySet` 内の全ての要素に対して ``save()`` メソッドを適用したければ、 単に全要素に渡って :meth:`~django.db.models.Model.save` を呼び出してください:: for item in my_queryset: item.save() モデルの中の別フィールドの値によって別フィールドをアップデートするには :ref:`F() objects ` も使えます。これは、特に 最新の値によってカウンタを増加させたい場合に使えます。例えば、 pingback のカウント数をブログのエントリごとに増加させるには:: >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1) しかしながら、 ``F()`` はフィルタ内と EXCLUDE 句とは 違うところもあり、 アップデートに ``F()`` オブジェクトを使った時には JOIN が使えません。 つまり、アップデートされるモデル内で参照できるフィールドのみを使えるという ことです。もし、 JOIN を ``F()`` オブジェクトで使おうとした場合、 ``FieldError`` が生じます。:: # THIS WILL RAISE A FieldError >>> Entry.objects.update(headline=F('blog__name')) .. _topics-db-queries-related: リレーション ============ モデル内でリレーション ( :class:`~django.db.models.ForeignKey` :class:`~django.db.models.OneToOneField` :class:`~django.db.models.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`` のモデルを使っています。 .. _descriptors: http://users.rcn.com/python/download/Descriptor.htm .. _`デスクリプタ`: descriptors_ 一対多のリレーション -------------------- 順方向 ~~~~~~ モデルに :class:`~django.db.models.ForeignKey` フィールドがある場合、 そのモデルのインスタンスは、単に属性を使ってリレーション先 (外部) の オブジェクトを参照できます。 例:: >>> e = Entry.objects.get(id=2) >>> e.blog # リレーション先の Blog オブジェクトを返します。 外部キー属性の値は取得 (get) も設定 (set) もできます。当然ながら、外部キー への変更は :meth:`~django.db.models.Model.save` を呼び出すまで データベースに反映されません。例えば:: >>> e = Entry.objects.get(id=2) >>> e.blog = some_blog >>> e.save() :class:`~django.db.models.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 # データベースは検索せず、キャッシュを使います。 クエリセットのメソッド :meth:`~django.db.models.query.QuerySet.select_related` を 使うと、一対多のリレーションのリレーション先オブジェクト全てをあらかじめ 再帰的にキャッシュに取り込みます。例えば:: >>> e = Entry.objects.select_related().get(id=2) >>> print e.blog # データベースを使わずにキャッシュを使います。 >>> print e.blog # データベースを使わずにキャッシュを使います。 .. _backwards-related-objects: 「逆方向の」リレーション ~~~~~~~~~~~~~~~~~~~~~~~~~~~ あるモデルが :class:`~django.db.models.ForeignKey` で別のモデルに リレーションを張っている場合、リレーションを張られた側のモデルのインスタンスは、 リレーションを張った側のモデルの全てのインスタンスを返すマネジャにアクセスでます。 リレーションを張っている側のモデル名を全て小文字にしたものを ``FOO`` とすると、 :class:`~django.db.models.Manager` の名前のデフォルト値は ``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() :class:`~django.db.models.ForeignKey` 逆参照は、 :class:`~django.db.models.Manager` としてのアクセスはできず、インスタンスとし てアクセスせねばなりません。例えば:: >>> Blog.entry_set Traceback: ... AttributeError: "Manager must be accessed via instance". 前述の「オブジェクトの取得」 で説明した :class:`~django.db.models.query.QuerySet` のメソッドに加えて、 :class:`~django.db.models.ForeignKey` で表現されるマネジャには、 リレーション元のオブジェクトの集合を扱うための追加のメソッドがあります。 メソッドの概要は以下に示しますが、詳細は :doc:`リレーションのリファレンス ` を参照してく ださい。 ``add(obj1, obj2, ...)`` 引数に指定したモデルオブジェクトを、リレーション元オブジェクトの集合に 加えます。 ``create(**kwargs)`` 新たなオブジェクトを生成して保存し、リレーション元オブジェクトの集合に 加えます。生成されたオブジェクトを返します。 ``remove(obj1, obj2, ...)`` リレーション元のオブジェクトの集合から、引数に指定したモデルオブジェク トを除去します。 ``clear()`` リレーション元オブジェクトの集合から、全てのオブジェクトを除去します。 リレーション先セットのメンバを一括で代入するには、イテレーション可能オブジェ クトを代入します。例えば:: b = Blog.objects.get(id=1) b.entry_set = [e1, e2] この例では、 ``e1`` と ``e2`` が Entry インスタンスになるか、 数値のプライマリーキーの値となります。 ``clear()`` メソッドを利用できる場合に代入を行うと、まず ``entry_set`` から 全てのオブジェクトを除去しておき、その後で右辺の iterable (上の例ではリスト) のオブジェクト追加します。 ``clear()`` メソッドを使えない場合は、 ``entry_set`` に既に存在するオブジェクトを削除せず、単に右辺の iterable 上 の全てのオブジェクトを追加します。 この節で説明した「逆方向の」操作は、いずれもデータベースを即時変更します。 操作結果は追加、新規作成、削除といった操作を行う度に即座に自動的にデータベー スに保存されます。 .. _m2m-reverse-relationships: 多対多のリレーション -------------------- 多対多のリレーションの場合、リレーションの関係にあるモデルの一方は、互いに もう一方にアクセスするための自動 API を獲得します。この API は一対多のリレー ションにおける「逆方向の」参照のように動作します。 一対多のリレーションとの唯一の違いは、属性の名前づけ規則です。 :class:`~django.db.models.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 オブジェクトを返します。 :class:`~django.db.models.ForeignKey` と同様、 :class:`~django.db.models.ManyToManyField` には ``related_name`` パラメタ を指定できます。 上の例で、 ``Entry`` の ``ManyToManyField`` に ``related_name='entries'`` を指定していた場合、 ``Author`` インスタンスは ``entry_set`` ではなく ``entries`` という属性を持つようになります。 一対一のリレーション ------------------------ 一対一 (one-to-one) のリレーションは多対多のリレーションと非常によく似てい ます。モデルに :class:`~django.db.models.OneToOneField` を定義すると、モデ ルのインスタンスはリレーション先のオブジェクトを簡単な属性アクセスで参照で きます。 例を示します:: class EntryDetail(models.Model): entry = models.OneToOneField(Entry) details = models.TextField() ed = EntryDetail.objects.get(id=2) ed.entry # リレーション先の Entry オブジェクトを返します。 多対多との違いは、逆参照のクエリです。多対多の時と同様、リレーション先のモ デルはリレーション元のモデルに対するマネジャオブジェクトにアクセスできます が、このマネジャはオブジェクトの集合ではなく単一のオブジェクトを表現してい ます:: e = Entry.objects.get(id=2) e.entrydetail # リレーション先の EntryDetail オブジェクトを返します。 逆リレーション先のオブジェクトが存在しなければ、 Django は ``DoesNotExist`` 例外を送出します。 逆参照のリレーションにインスタンスを代入すると、順参照のリレーションと同じ ようにリレーション先を変更できます:: e.entrydetail = ed リレーションの後方参照はどうやって実現されているのか ---------------------------------------------------- 他のオブジェクトリレーショナルマッパでは、双方向からリレーションを定義せね ばなりません。 Django の開発者たちはこれを DRY 則 (Don't Repeat Yourself) の侵犯だと考えたため、 Django では片方だけでリレーションを定義すればよいよ うにしています。 しかし、なぜあるモデルクラスが、自分に対してリレーションを張っているモデル クラスのことを、そのクラスがロードされる前に検知できるのでしょうか? 答えは :setting:`INSTALLED_APPS` 設定にあります。最初にモデルをロードした時 に、 Django は :setting:`INSTALLED_APPS` の全てのモデルを走査して、必要に応 じて後方参照をメモリ中に作成します。 :setting:`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 を直接使ったクエリ 生 SQL へのフォールバック ========================= Django のデータベースマッパで扱うには複雑すぎる SQL 文を書かねばならないよ うな場合には、生の SQL 文実行モードを使えます。Django は生 SQL 文をクエリ とうる方法を二つ持っています。 :doc:`/topics/db/sql` を見てください。 生 raw-SQL 文実行モードの使い方としてお勧めなのは、そのようなクエリ文を実行 するモデルのカスタムメソッドやカスタムマネジャのメソッドを実装するというも のです。 Django はモデルレイヤでデータベースクエリを記述するように *何ら要求してはいません* が、このアプローチをとることで、データアクセスのた めのロジックを一箇所にまとめられるので、コード組織化の観点からスマートにな ります。詳しい説明は :ref:`topics-db-sql` を参照してください。 最後に、 Django のデータベースレイヤは単にデータベースへの一つのインタフェー スに過ぎないということに注意しておきましょう。データベースには他のツールや プログラム言語、データベースフレームワークを介してアクセスできます。データ ベースについて Django 固有の何かがあるわけではないのです。