2013-06-16 12 views
11

Djangoのfilter()メソッドがどのように動作するのかについて、非常に基本的で根本的なものがないと思います。次のモデルの使用関連モデル分野のDjango filter()

class Collection(models.Model): 
    pass 

class Item(models.Model): 
    flag = models.BooleanField() 
    collection = models.ForeignKey(Collection) 

と質問の下部に移入()関数を呼び出すことにより、供給されたデータとを、./manage.pyシェルで以下を実行してみてください。

len(Collection.objects.filter(item__flag=True)) 

これは、flag = Trueのアイテムを少なくとも1つ持つコレクションの数である "2"を出力するという私の期待でした。この期待は、https://docs.djangoproject.com/en/1.5/topics/db/queries/#lookups-that-span-relationshipsのドキュメントに基づいています。この例では、「この例では、名前が「Beatles Blog」のブログですべてのEntryオブジェクトが取得されます。

しかし、上記の呼び出しは実際にはflag = TrueのItemレコードの数である "6"を出力します。実際に返されるオブジェクトはCollectionオブジェクトです。同じCollectionオブジェクトを複数回返すようですが、flag = Trueの対応するItemレコードごとに1回です。これは、次のように確認できます。

queryset = Collection.objects.filter(item__flag=True) 
queryset[0] == queryset[1] 

これはTrueを出力します。

これは正しい動作ですか?もしそうなら、根拠は何ですか?予想されるものであれば、ドキュメントは厳密に正しいと解釈することができますが、各オブジェクトを複数回返すことができるとは言いません。

これは、非常に驚​​くべき(またはまったく間違った)動作であるように見える関連する例です。 )これは、除外()コールは、カスタムモデルマネージャによって追加された場合には私を捕まえて、発信者は、次いで、(フィルタを追加した

from django.db.models import Count  
[coll.count for coll in Collection.objects.filter(item__flag=True).annotate(count=Count("item"))] 
[coll.count for coll in Collection.objects.exclude(item=None).filter(item__flag=True).annotate(count=Count("item"))] 

第一ケースプリント「[2,4]」しかし、2枚目に "[8,16]"という文字が印刷されます。

移入機能:

def populate(): 
    Collection.objects.all().delete() 

    collection = Collection() 
    collection.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 

    collection = Collection() 
    collection.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 
    item = Item(collection=collection, flag=True) 
    item.save() 

    collection = Collection() 
    collection.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 
    item = Item(collection=collection, flag=False) 
    item.save() 

答えて

12

それは、この2つの部分がありますが判明。まず、distinct()メソッドがあります。このメソッドについては、次のようになります。

デフォルトでは、QuerySetは重複する行を排除しません。実際には、 Blog.objects.all()などの単純なクエリでは、結果が重複する可能性があります。 行が導入されることはほとんどありません。ただし、クエリが複数のテーブルにまたがる場合は、クエリセットの評価時に の結果が重複する可能性があります。それはあなたが distinct()を使用したときです。

次の出力予想通り "2":

len(Collection.objects.filter(item__flag=True).distinct()) 

しかし、これは)(注釈を使用して、私が与えた、より複雑な例では役立ちません。これは既知の問題の例であることが判明しました:https://code.djangoproject.com/ticket/10060