======== マネジャ ======== :revision-up-to: 17812 (1.4) unfinished .. class:: Manager() マネジャ (``Manager``) とは、データベースクエリ操作を Django モデルに提供し ているインタフェースです。 Django アプリケーション内のモデルは全て少なくと も一つマネジャを備えています。 マネジャクラス ``Manager`` の動作はデータベース API ドキュメントの :doc:`/topics/db/queries` で説明していますが、この節ではマネジャの挙動をカス タマイズするためのモデルオプションについて具体的に触れます。 .. _manager-names: マネジャの名前 ================ デフォルトでは、 Django は全ての Django モデルクラスに ``object`` という名 前で ``Manager`` オブジェクトを追加します。ただし、 ``objects`` をフィール ド名として使いたい場合や、マネジャに ``objects`` 以外の名前をつけたい場合に は、モデルごとに名前を変えてやる必要があります。クラス中でマネジャの名前を 変更するには、 ``models.Manager()`` 型のクラス属性を定義します。例えば:: from django.db import models class Person(models.Model): #... people = models.Manager() このようにすると、 ``Person.people.all()`` が ``Person`` オブジェクトのリス トを提供し、 ``Person.objects`` の参照は ``AttributeError`` 例外を送出します。 .. _custom-managers: カスタムマネジャ ================= ベースクラスの ``Manager`` クラスを拡張して、モデル中でカスタムのマネジャを インスタンス化すれば、モデルでカスタムのマネジャを使えます。 マネジャをカスタマイズする理由は大きく分けて二つあります。一つはマネジャに 追加のメソッドを持たせたい場合、もう一つはマネジャの返す初期 ``QuerySet`` を変更したい場合です。 追加のマネジャメソッド定義 ---------------------------- モデルに「テーブルレベル」の機能を持たせたい場合、マネジャへのメソッドは良 い方法です。 (「行レベル」の機能を持たせたい、例えばモデルオブジェクトの個々 のインスタンスに影響する関数を実装したい場合には、カスタムのマネジャメソッ ドではなく :ref:`モデルのメソッド ` を使って下さい。) カスタムのマネジャメソッドは何を返してもかまいません。 ``QuerySet`` を返さ なくてもよいのです。 例えば、以下のカスタムマネジャでは、 ``with_counts()`` というメソッドを提供 しています。このメソッドは全ての ``OpinionPoll`` オブジェクトからなるリスト を返しますが、その際に集約クエリの結果である ``num_responses`` という追加の 属性を追加します:: class PollManager(models.Manager): def with_counts(self): from django.db import connection cursor = connection.cursor() cursor.execute(""" SELECT p.id, p.question, p.poll_date, COUNT(*) FROM polls_opinionpoll p, polls_response r WHERE p.id = r.poll_id GROUP BY 1, 2, 3 ORDER BY 3 DESC""") result_list = [] for row in cursor.fetchall(): p = self.model(id=row[0], question=row[1], poll_date=row[2]) p.num_responses = row[3] result_list.append(p) return result_list class OpinionPoll(models.Model): question = models.CharField(max_length=200) poll_date = models.DateField() objects = PollManager() class Response(models.Model): poll = models.ForeignKey(Poll) person_name = models.CharField(max_length=50) response = models.TextField() この例では、 ``OpinionPoll.objects.with_counts()`` を使うと、 ``num_responses`` 属性を備えた ``OpinionPoll`` オブジェクトのリストを返しま す。 この例でもう一つ注意して欲しいのは、マネジャメソッドが自分の属しているモデ ルクラスを取り出すために ``self.model`` にアクセスできるという点です。 初期 QuerySet の変更 ---------------------- マネジャのベースの ``QuerySet`` は、システム上の全てのオブジェクトを返しま す。例えば、以下のモデル:: class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) では、 ``Book.objects.all()`` とすると、データベース上の全ての books を返し ます。 ``Manager.get_query_set()`` メソッドをオーバライドすれば、 ``Manager`` のベー スの ``QuerySet`` をオーバライドできます。 ``get_query_set()`` は必要なプロ パティを備えた ``QuerySet`` を返さねばなりません。 例えば、以下のモデルには *二つの* マネジャがあります。片方は全てのオブジェ クトを返し、もう一方は Roald Dahl の書いた本だけを返します:: # まず Manager のサブクラスを定義します。 class DahlBookManager(models.Manager): def get_query_set(self): return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl') # 次に Book モデルに明示的にフックします。 class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) objects = models.Manager() # デフォルトマネジャ。 dahl_objects = DahlBookManager() # Dahl 縛りのマネジャ。 このモデル例では、 ``Book.objects.all()`` はデータベース上の Book を全て返 しますが、 ``Book.dahl_objects.all()`` は Roald Dahl の書いた本だけを返しま す。 ``get_query_set()`` は ``QuerySet`` オブジェクトを返すので、もちろん ``filter()`` や ``exclude()`` をはじめ全ての ``QuerySet`` メソッドを使えま す。従って、以下のような文を実行できます:: Book.dahl_objects.all() Book.dahl_objects.filter(title='Matilda') Book.dahl_objects.count() この例はもう一つ興味深いテクニックの存在を示しています。それは同じモデルで 複数のマネジャを使えるということです。モデルには、好きなだけマネジャのイン スタンスをアタッチできます。この手法を使うと、よく利用するフィルタをモデル に簡単に実装できます。 例えば:: class MaleManager(models.Manager): def get_query_set(self): return super(MaleManager, self).get_query_set().filter(sex='M') class FemaleManager(models.Manager): def get_query_set(self): return super(FemaleManager, self).get_query_set().filter(sex='F') class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female'))) people = models.Manager() men = MaleManager() women = FemaleManager() この例では、 ``Person.men.all()``, ``Person.women.all()``, ``Person.people.all()`` といったクエリを発行できるようになっており、期待通 りの結果を得られます。 カスタムのマネジャオブジェクトを使う場合、 Django がモデル内に最初に見つけ たマネジャ (モデルに定義した順番で最初のマネジャ) は特別扱いされるというこ とに注意してください。 Django はクラス内で最初に見つけたマネジャを「デフォ ルトの」マネジャにし、このデフォルトマネジャを ( :djadmin:`dumpdata` を含 む) あちこちでモデルのマネジャとして使います。ですから、 ``get_query_set()`` のオーバライドによって、扱いたいオブジェクトを取り出せ なくなるような羽目に陥らないように、デフォルトマネジャの選択には細心の注意 を払ってください。 .. _managers-for-related-objects: マネジャを使ってリレーション先のオブジェクトにアクセスする ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ デフォルトの動作では、 Django は (``choice.poll`` のような) リレーション先 のオブジェクトにアクセスするときに、リレーション先のデフォルトマネジャでは なく、「素の」マネジャを使います。というのも、 Django はリレーション先のオ ブジェクトを (たとえデフォルトマネジャによって除外され、通常はアクセスでき ないオブジェクトだとしても) 取り出せるようにしておく必要があるからです。 通常のプレーンなマネジャ ( :class:`django.db.models.Manager` ) があなたの 環境にふさわしく無いのであれば、 マネジャクラスに `use_for_related_fields` を定義することであなたのモデルのデフォルトとして同じクラスを使うように Django に対して制約をかけることができます。 これは `下記`_ に詳しくドキュメントされています。 .. _下記: manager-types_ .. _custom-managers-and-inheritance: カスタムマネジャとモデルの継承 ------------------------------------- クラスの継承とモデルのマネジャの関係は、お互い完全にしっくりきているわけで はありません。マネジャはたいていあるクラス固有のものなので、サブクラスでマ ネジャを継承するのは必ずしもよいアイデアとはいえないからです。また、最初に 宣言されたマネジャが*デフォルトマネジャ* になることから、デフォルトマネジャ の制御が重要になってきます。そこで、ここでは、 Django がカスタムマネジャと :ref:`モデル継承 ` をどのように扱うか説明します: 1. 抽象ベースクラスでないクラスで定義されたマネジャは、他のクラスに継承 *されません* 。非抽象ベースクラスのマネジャを再利用したければ、 子クラスで明示的に宣言してください。この種のマネジャは、たいていマネ ジャを定義しているクラス固有のもので、(デフォルトマネジャとして) 継 承すると思わぬ結果を招くことがあるからです。そのため、子クラスに自動 的に継承されないのです。 2. 抽象ベースクラスのマネジャは、通常の Python の名前解決規則 (name resolution order, 子クラスの名前が他の名前をオーバライドする、 親クラスリストの最初の親クラスの名前から順に参照する、など) に基づい て、常に子クラスに継承されます。抽象ベースクラスは、子クラスに共通し て持たせたい情報やふるまいを保持するためのクラスだからです。共通のマ ネジャの定義は、共通の情報として親クラスに置くのが適切です。 3. デフォルトマネジャは、そのクラスで最初に宣言されたマネジャクラスか、 最初に見付かった抽象ベースクラスのデフォルトマネジャです。明示的なデ フォルトマネジャの設定がなければ、 Django の通常のデフォルトマネジャ を使います。 これらのルールはモデルのグループに対してカスタムマネジャのコレクションを インストールしたいときに十分なフレキシビリティを提供しています。 例えば、このようなベースクラスがあるとします :: class AbstractBase(models.Model): ... objects = CustomManager() class Meta: abstract = True サブクラスでこれを直接使うのであれば、 ベースクラスでマネジャを定義して いないので ``objects`` がデフォルトのマネジャ になります。 :: class ChildA(AbstractBase): ... # このクラスは CustomManager をデフォルトマネジャとして持っています ``AbstractBase`` から継承するけれども別のマネジャをデフォルトで用意する のであれば、子クラスにデフォルトマネジャを提供することが出来ます:: class ChildB(AbstractBase): ... # 明示的なデフォルトマネジャ default_manager = OtherManager() ここで、 ``default_manager`` がデフォルトです。 ``objects`` マネジャは 引き続き利用できます。というのは継承されているからです。デフォルトとして 使われないというだけです。 この例の最後として、子クラスにマネジャを追加したいけれども、 ``AbstractBase`` のデフォルトを引き続き利用したいとします。子クラスに 新しいマネジャを直接追加することができません。 というのはそうするとデフォルトを上書いてしまい、抽象基底クラスの全ての マネジャを明示的に含めなければならなくなるからです。 解決法は他の基底クラスにマネジャを追加して、デフォルトの後に継承階層に 含めることです :: class ExtraManager(models.Model): extra_manager = OtherManager() class Meta: abstract = True class ChildC(AbstractBase, ExtraManager): ... # デフォツオマネージャは CustomManagerですが、 # OtherManagerも"extra_manager"属性として利用可能です。 実装する際に考慮すること ------------------------------------ カスタム ``Manager`` にどんな機能を追加したにせよ、 ``Manager`` インスタンスのシャロウコピー (shallow copy)を可能にしなければなりませ; すなわち、以下のコードは正しく動く必要があります :: >>> import copy >>> manager = MyManager() >>> my_copy = copy.copy(manager) Django はある種のクエリの最中にマネジャをシャロウコピーさせます; もしも Manager がコピーされないのであればそのようなクエリは失敗します。 これはほとんどのカスタムマネジャにとっては問題とはならないでしょう。 自分の ``Manager`` に簡単なメソッドを追加するだけであれば、 うっかり ``Manager`` インスタンスをコピーさせないようにしてしまう、という ことはあり得ないでしょう。しかし、 オブジェクトの状態を管理するために ``Manager`` オブジェクトの ``__getattr__`` やその他のプライベートメソッド をオーバーライドするのであれば、 ``Manager`` がコピーされる能力に影響を 与えないようにするべきです。 .. _manager-types: 自動マネジャタイプの制御 =================================== このドキュメントはDjangoがマネジャクラスを作る際にすでに何度も触れられて きました: `関連オブジェクトにアクセスする`_ ための `デフォルトマネジャ`_ と "プレーン" マネジャ、です。 Djangoの実装においては、一時的なプレーン マネジャが必要な箇所がいくつかあります。これらの自動的に作成されるマネジャ は通常は :class:`django.db.models.Manager` のインスタンスです。 .. _デフォルトマネジャ: manager-names_ .. _関連オブジェクトにアクセスする: managers-for-related-objects_ この章では "自動マネジャ( automatic manager )" という用語を、 Django が モデルマネジャを自動的に作成してくれる、という意味で使います。 -- マネジャ無しのモデルのデフォルトマネジャの場合もありますし、関連オブジェクト にアクセスする際に一時的に使われたりもします。 このデフォルトクラスが正しい選択では無い場合もあります。 Django自体では 提供されていない `django.contrib.gis` アプリケーションに1つの例があります。 データベースと円滑に相互作用するためには、特殊なクエリセット (``GeoQuerySet``)を使わなくてはならないので、 全ての `gis` モデルは特殊な マネジャクラス(``GeoManager``)を使わなくてはいけません。 このような特殊なマネジャを必要とするモデルは自動マネジャが作成されるときには 必ず同じマネジャクラスを使う必要があります。 Django にはカスタムマネジャ開発者に対して、モデルのデフォルトマネジャが作成 される際には必ず自分の作成したマネジャクラスが自動マネジャとして使われるよう に指定できるような方法が用意されています。これは、マネジャクラスに ``use_for_related_fields`` 属性を設定することで実現されます :: class MyManager(models.Manager): use_for_related_fields = True ... この属性がモデルの *デフォルト* マネジャに設定される(デフォルトマネジャだけ がこのような状況になり得ます)と、Django はクラスに対して自動的にマネジャを 作成する必要になると必ずこのクラスを使うようになります。 それ以外の場合は、 :class:`django.db.models.Manager` が使われます。 .. admonition:: 歴史的ノート 使われる目的を考えると、この属性名(``use_for_related_fields``)は 少し変です。元々は、この属性は関連フィールドのアクセスだけに使われる ようなマネジャを制御していましたのでこういう名前になったのです。 コンセプトが明確になるにつれてますます広い用途で使われるようになり ましたが、名前は変更されていません。これは第一に既存のコードが 将来の Django のバージョンに置いても :ref:`継続し動作する ` ようにするためです。 自動マネジャインスタンスに使えるように正しくマネジャを書く --------------------------------------------------------------- 上記の `django.contrib.gis` の例ですでに触れたように、 ``use_for_related_fields`` は主にカスタム ``QuerySet`` サブクラス を返さなければならないマネージャのための機能です。 あなたのマネジャでこの機能を提供する場合、いくつか覚えておかなければ ならないことがありますので、このセクションで示します。 この種のマネジャのサブクラスでは結果をフィルタしてはいけない ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 自動マネジャが使われる一つの理由は、他のモデルから関連したオブジェクトに アクセスする場合です。この場合、Djangoはフェッチしようとしているモデル の全てのオブジェクトを見る必要があるので、 参照される *全て* が取得 される必要があるのです。 もしも ``get_query_set()`` メソッドをオーバーライドして、列をフィルター すれば Django は正しくない結果を返します。これをやってはいけません。 ``get_query_set()`` の結果をフィルタするマネジャは自動マネジャとしては ふさわしくありません。 クラスを定義するときは ``use_for_related_fields`` を設定すること ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``use_for_related_fields`` 属性はマネージャーの *クラス* に設定されなければ なりません。 *インスタンス* にではありません。前の例ではそれを設定する正しい 方法を示していましたが、以下のコードは正しく動作しません :: # BAD: 不正なコード class MyManager(models.Manager): ... # MyManagerインスタンスに属性を設定します。 # Django はこの設定を無視します mgr = MyManager() mgr.use_for_related_fields = True class MyModel(models.Model): ... objects = mgr # 不正なコードの終了 モデルで既に使用されている後でクラスオブジェクトの属性を変更することも やってはいけません。なぜならば属性値はモデルクラスが作成された時に既に 処理されていて、再読み込みは行われないからです。 マネージャクラスが最初に定義されたときに属性を設定すれば、この章の最初の 例で示したように全てスムーズに動作します。