マネジャ

revision-up-to:11321 (1.1) unfinished
class Manager

マネジャ (Manager) とは、データベースクエリ操作を Django モデルに提供し ているインタフェースです。 Django アプリケーション内のモデルは全て少なくと も一つマネジャを備えています。

マネジャクラス Manager の動作はデータベース API ドキュメントの クエリを生成する で説明していますが、この節ではマネジャの挙動をカス タマイズするためのモデルオプションについて具体的に触れます。

マネジャの名前

デフォルトでは、 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 例外を送出します。

カスタムマネジャ

ベースクラスの Manager クラスを拡張して、モデル中でカスタムのマネジャを インスタンス化すれば、モデルでカスタムのマネジャを使えます。

マネジャをカスタマイズする理由は大きく分けて二つあります。一つはマネジャに 追加のメソッドを持たせたい場合、もう一つはマネジャの返す初期 QuerySet を変更したい場合です。

追加のマネジャメソッド定義

モデルに「テーブルレベル」の機能を持たせたい場合、マネジャへのメソッドは良 い方法です。 (「行レベル」の機能を持たせたい、例えばモデルオブジェクトの個々 のインスタンスに影響する関数を実装したい場合には、カスタムのマネジャメソッ ドではなく モデルのメソッド を使って下さい。)

カスタムのマネジャメソッドは何を返してもかまいません。 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 はクラス内で最初に見つけたマネジャを「デフォ ルトの」マネジャにし、このデフォルトマネジャを (admin アプリケーション以外 でも) あちこちでモデルのマネジャとして使います。ですから、 get_query_set() のオーバライドによって、扱いたいオブジェクトを取り出せ なくなるような羽目に陥らないように、デフォルトマネジャの選択には細心の注意 を払ってください。

カスタムマネジャとモデルの継承

クラスの継承とモデルのマネジャの関係は、お互い完全にしっくりきているわけで はありません。マネジャはたいていあるクラス固有のものなので、サブクラスでマ ネジャを継承するのは必ずしもよいアイデアとはいえないからです。また、最初に 宣言されたマネジャが*デフォルトマネジャ* になることから、デフォルトマネジャ の制御が重要になってきます。そこで、ここでは、 Django がカスタムマネジャと モデル継承 をどのように扱うか説明します:

  1. 抽象ベースクラスでないクラスで定義されたマネジャは、他のクラスに継承 されません 。非抽象ベースクラスのマネジャを再利用したければ、 子クラスで明示的に宣言してください。この種のマネジャは、たいていマネ ジャを定義しているクラス固有のもので、(デフォルトマネジャとして) 継 承すると思わぬ結果を招くことがあるからです。そのため、子クラスに自動 的に継承されないのです。
  2. 抽象ベースクラスのマネジャは、通常の Python の名前解決規則 (name resolution order, 子クラスの名前が他の名前をオーバライドする、 親クラスリストの最初の親クラスの名前から順に参照する、など) に基づい て、常に子クラスに継承されます。抽象ベースクラスは、子クラスに共通し て持たせたい情報やふるまいを保持するためのクラスだからです。共通のマ ネジャの定義は、共通の情報として親クラスに置くのが適切です。
  3. デフォルトマネジャは、そのクラスで最初に宣言されたマネジャクラスか、 最初に見付かった抽象ベースクラスのデフォルトマネジャです。明示的なデ フォルトマネジャの設定がなければ、 Django の通常のデフォルトマネジャ を使います。

These rules provide the necessary flexibility if you want to install a collection of custom managers on a group of models, via an abstract base class, but still customize the default manager. For example, suppose you have this base class:

class AbstractBase(models.Model):
    ...
    objects = CustomerManager()

    class Meta:
        abstract = True

If you use this directly in a subclass, objects will be the default manager if you declare no managers in the base class:

class ChildA(AbstractBase):
    ...
    # This class has CustomManager as the default manager.

If you want to inherit from AbstractBase, but provide a different default manager, you can provide the default manager on the child class:

class ChildB(AbstractBase):
    ...
    # An explicit default manager.
    default_manager = OtherManager()

Here, default_manager is the default. The objects manager is still available, since it's inherited. It just isn't used as the default.

Finally for this example, suppose you want to add extra managers to the child class, but still use the default from AbstractBase. You can't add the new manager directly in the child class, as that would override the default and you would have to also explicitly include all the managers from the abstract base class. The solution is to put the extra managers in another base class and introduce it into the inheritance hierarchy after the defaults:

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    ...
    # Default manager is CustomManager, but OtherManager is
    # also available via the "extra_manager" attribute.

Controlling Automatic Manager Types

This document has already mentioned a couple of places where Django creates a manager class for you: default managers and the "plain" manager used to access related objects. There are other places in the implementation of Django where temporary plain managers are needed. Those automatically created managers will normally be instances of the django.db.models.Manager class.

Throughout this section, we will use the term "automatic manager" to mean a manager that Django creates for you -- either as a default manager on a model with no managers, or to use temporarily when accessing related objects.

Sometimes this default class won't be the right choice. One example is in the django.contrib.gis application that ships with Django itself. All gis models must use a special manager class (GeoManager) because they need a special queryset (GeoQuerySet) to be used for interacting with the database. It turns out that models which require a special manager like this need to use the same manager class wherever an automatic manager is created.

Django provides a way for custom manager developers to say that their manager class should be used for automatic managers whenever it is the default manager on a model. This is done by setting the use_for_related_fields attribute on the manager class:

class MyManager(models.Manager):
    use_for_related_fields = True

    ...

If this attribute is set on the default manager for a model (only the default manager is considered in these situations), Django will use that class whenever it needs to automatically create a manager for the class. Otherwise, it will use django.db.models.Manager.

Historical Note

Given the purpose for which it's used, the name of this attribute (use_for_related_fields) might seem a little odd. Originally, the attribute only controlled the type of manager used for related field access, which is where the name came from. As it became clear the concept was more broadly useful, the name hasn't been changed. This is primarily so that existing code will continue to work in future Django versions.

Writing Correct Managers For Use In Automatic Manager Instances

As already suggested by the django.contrib.gis example, above, the use_for_related_fields feature is primarily for managers that need to return a custom QuerySet subclass. In providing this functionality in your manager, there are a couple of things to be remember and that's the topic of this section.

Do not filter away any results in this type of manager subclass

One reason an automatic manager is used is to access objects that are related to from some other model. In those situations, Django has to be able to see all the objects for the model it is fetching, so that anything which is referred to can be retrieved.

If you override the get_query_set() method and filter out any rows, Django will return incorrect results. Don't do that. A manager that filters results in get_query_set() is not appropriate for use as an automatic manager.