19

ドキュメントからそれを把握することはできません。具体的には、私にはまったくわかりません。

  • それはグローバルな設定ですか?したがって、この属性をモデルマネージャーの1つで指定すると、すべてのモデルクラスでグローバルに使用されますか?
  • グローバル設定でない場合、どの関係が正確に影響を受けますか?
  • 同じモデルのある関係に1つのモデルマネージャーを配置し、別の関係に別のモデルマネージャーを配置することは可能ですか?

ドキュメントにはそれらのafaikが欠けているので、何よりも私は良い最小限の使用例をいただければ幸いです。ありがとう。

4

2 に答える 2

51

それはグローバルな設定ですか?したがって、この属性をモデルマネージャーの1つで指定すると、すべてのモデルクラスでグローバルに使用されますか?

あなたがグローバルとはどういう意味かを私が理解していれば、答えはノーです。デフォルトのマネージャー(クラスで指定された最初のマネージャー)に設定されている場合にのみ、クラスに使用されます。いくつかのモデルでマネージャーを再利用できます。属性は、デフォルトのマネージャーであったクラスにのみ影響します。

これは、例が理解に役立つと私が思ったものです。1対1の関係でリンクされた、次のメンバーモデルとプロファイルモデルを使用してみましょう。

from django.db import models  

class Member(models.Model):
    name = models.CharField(max_length=100)
    active = models.BooleanField()

    def __unicode__(self):
        return self.name


class Profile(models.Model):
    member = models.OneToOneField(Member)
    age = models.PositiveIntegerField()

    def __unicode__(self):
        return str(self.age)

アクティブなジョンと非アクティブなフィルの2人のメンバーを作成し、それぞれのプロファイルを設定します。

>>> m1 = Member(name='John', active=True)
>>> m1.save()
>>> p1 = Profile(member=m1, age=18)
>>> p1.save()
>>> m2 = Member(name='Phil', active=False)
>>> m2.save()
>>> p2 = Profile(member=m2, age=35)
>>> p2.save()

Djangoが関係を保存する方法

まず、Djangoがどのように関係を保存するかを見てみましょう。Johnのプロファイルを取得し、その名前空間を確認します。

>>> p = Profile.objects.get(id=1)
>>> p.__dict__
{'age': 18, '_state': <django.db.models.base.ModelState object at 0x95d054c>, 'id': 1, 'member_id': 1}

ここでは、メンバーインスタンスのID値を格納することによって関係が定義されていることがわかります。属性を参照するときmember、Djangoはマネージャーを使用してデータベースからメンバーの詳細を取得し、インスタンスを作成します。ちなみに、この情報は、再度使用する場合に備えてキャッシュされます。

>>> p.member
<Member: John>
>>> p.__dict__
{'age': 18, '_member_cache': <Member: John>, '_state': <django.db.models.base.ModelState object at 0x95d054c>, 'id': 1, 'member_id': 1}

どのマネージャーを使用するか

特に明記されていない限り、Djangoは、モデルに追加されたカスタムマネージャーではなく、この関係ルックアップに標準マネージャーを使用します。たとえば、アクティブなメンバーのみを返すように次のマネージャーを作成したとします。

class ActiveManager(models.Manager):
    def get_query_set(self):
        return super(ActiveManager, self).get_query_set().filter(active=True)

次に、それをデフォルトマネージャーとしてメンバーモデルに追加しました(実際には、管理コマンドなどの多くのユーティリティがデフォルトマネージャーのみを使用するため、これはBadIdea™でdumpdataあり、したがって、フィルターで除外されるデフォルトマネージャーです。インスタンスは、データの損失または同様の厄介な副作用につながる可能性があります):

class Member(models.Model):
    name = models.CharField(max_length=100)
    active = models.BooleanField()

    objects = ActiveManager()

    def __unicode__(self):
        return self.name

マネージャが非アクティブなユーザーを除外するようになったため、データベースからJohnのメンバーシップのみを取得できます。

>>> Member.objects.all()
[<Member: John>]

ただし、両方のプロファイルを使用できます。

>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]

したがって、リレーションシップルックアップはカスタムマネージャーではなく標準マネージャーを使用するため、プロファイルを介して非アクティブなメンバーにアクセスできます。

>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

ただし、use_for_related_fieldsマネージャーに属性を設定すると、関係のルックアップにこのマネージャーを使用する必要があることがDjangoに通知されます。

class ActiveManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        return super(ActiveManager, self).get_query_set().filter(active=True)

したがって、Philのプロファイルからメンバーシップレコードを取得することはできなくなります。

>>> p = Profile.objects.get(id=2)
>>> p.member
---------------------------------------------------------------------------
DoesNotExist                              Traceback (most recent call last)

/home/blair/<ipython console> in <module>()

/usr/lib/pymodules/python2.6/django/db/models/fields/related.pyc in __get__(self, instance, instance_type)
    298             db = router.db_for_read(self.field.rel.to, instance=instance)
    299             if getattr(rel_mgr, 'use_for_related_fields', False):
--> 300                 rel_obj = rel_mgr.using(db).get(**params)
    301             else:
    302                 rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)

/usr/lib/pymodules/python2.6/django/db/models/query.pyc in get(self, *args, **kwargs)
    339         if not num:
    340             raise self.model.DoesNotExist("%s matching query does not exist."
--> 341                     % self.model._meta.object_name)
    342         raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"
    343                 % (self.model._meta.object_name, num, kwargs))

DoesNotExist: Member matching query does not exist.

これは、カスタムマネージャーがモデルのデフォルトマネージャーである場合(つまり、最初に定義されたマネージャーである場合)にのみ効果があることに注意してください。それでは、標準マネージャーをデフォルトマネージャーとして使用し、カスタムマネージャーをセカンダリマネージャーとして使用してみましょう。

class Member(models.Model):
    name = models.CharField(max_length=100)
    active = models.BooleanField()

    objects = models.Manager()
    active_members = ActiveManager()

    def __unicode__(self):
        return self.name

2人のマネージャーは、メンバーを直接見ると期待どおりに機能します。

>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]

また、デフォルトのマネージャーはすべてのオブジェクトを取得できるため、リレーションシップルックアップも成功します。

>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

現実

これまでのところ、サンプルモデルに1対1の関係を選択した理由がわかります。実際には(そしてドキュメントと矛盾して)、use_for_related_fields属性は1対1の関係にのみ使用されることがわかりました。外部キーと多対多の関係はそれを無視します。これはDjangoトラッカーのチケット#14891です。

同じモデルのある関係に1つのモデルマネージャーを配置し、別の関係に別のモデルマネージャーを配置することは可能ですか?

いいえ。ただし、上記のバグについてのこの議論では、これは将来の可能性として浮かび上がってきました。

于 2011-05-20T12:47:14.043 に答える
2

簡単に言うと、このバグが修正されるまで、「use-for-related-fields」は1対1の関係を除いて、djangoでは機能しないため、ユースケースがm2mまたはm2oneの場合は気にしないでください。またはあなたは失望するでしょう。

于 2013-03-20T03:20:42.933 に答える