2010-12-10 9 views
18

私は2つのカスタムマネージャメソッドを持つDjangoモデルを持っています。それぞれは、オブジェクトの異なるプロパティに基づいて、モデルのオブジェクトの異なるサブセットを返します。2つのDjangoクエリーセットの共通部分を見つけるにはどうしたらいいですか?

class FeatureManager(models.Manager): 

    def without_test_cases(self): 
     return self.get_query_set().annotate(num_test_cases=models.Count('testcase_set')).filter(num_test_cases=0) 

    def standardised(self): 
     return self.get_query_set().annotate(standardised=Count('documentation_set__standard')).filter(standardised__gt=0) 

(いずれもtestcase_setdocumentation_setが他のモデルにManyToManyField秒を参照してください。)

クエリセット、またはオブジェクトのリストだけを取得する方法があり、それは各によって返されたクエリセットのintersectiondですマネージャメソッド?

+0

に利用可能です各マネージャの2つのフィルタ機能を組み合わせることを止めているのは何ですか? –

+0

'Model.objects.managerMethodOne()。managerMethodTwo()'のような意味ですか?それはうまくいかないようでした。多分私はマネージャメソッドを正しく書いていないでしょうか? –

+3

フィルタ自体が機能します。 'Model.objects.filter(this = that).filter(that = somethingelse)'である。なぜあなたはそれをやっていないのですか? –

答えて

3

リファクタリング

class FeatureManager(models.Manager): 

    @staticmethod 
    def _test_cases_eq_0(qs): 
     return qs.annotate(num_test_cases=models.Count('testcase_set')).filter(num_test_cases=0) 

    @staticmethod 
    def _standardized_gt_0(qs): 
     return qs.annotate(standardised=Count('documentation_set__standard')).filter(standardised__gt=0) 

    def without_test_cases(self): 
     return self._test_cases_eq_0(self.get_query_set()) 

    def standardised(self): 
     return self._standardized_gt_0(self.get_query_set()) 

    def intersection(self): 
     return self._test_cases_eq_0(self._standardized_gt_0(self.get_query_set())) 
+0

ああ!ええ、それは賢いです、私は私のデザインが問題かもしれないと思っていた。 –

+7

彼の問題を解決するかどうか、それはまだGoogleが "querysetsのdjango交差点"の検索で返された最初のリンクだった2つのクエリセットの交差点を見つける方法には答えていません – johannestaas

0

一つの方法は、Pythonセットモジュールを使用することとちょうど交差を行うことができる。

ID = 5で重複クエリセットのカップルを作る:最初

In [42]: first = Location.objects.filter(id__lt=6) 
In [43]: last = Location.objects.filter(id__gt=4) 

「インポートセット」(非難の警告を受ける...うーん...ああ)。今、それらを構築し、交差する - 私たちは、セット内の1つの要素を取得:

In [44]: sets.Set(first).intersection(sets.Set(last)) 
Out[44]: Set([<Location: Location object>]) 

が今では本当に5で確認するために、交差要素のIDを取得する:

In [48]: [s.id for s in sets.Set(first).intersection(sets.Set(last))] 
Out[48]: [5] 

これは明らかに、データベースを二回ヒットすると、クエリセットのすべての要素を返します。より良い方法は、マネージャのフィルタを連鎖させることであり、それは1つのDBヒットとSQLレベルでそれを実行できるはずです。私はQuerySetやQuerySetメソッドを見ることができません。これは非常によく文書化されていない

intersection = Model.objects.filter(...) & Model.objects.filter(...) 

が、ほぼ正確に両方から条件に使用した条件のように振る舞うべきである。(クエリセットの一部を「設定」活用)

+1

'sets'を使わないでください。それは廃止されました。組み込みの 'set'(不変の場合は' frozenset')が良いです。 –

40

ほとんどの場合、あなただけ書くことができますクエリ。関連コード:https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203

+0

ええ、私はそれを試みたが、それは動作していないようだ。私はちょうど、より大きなクエリーセットにないものを含め、より小さなクエリーセットからすべてのオブジェクトを含むクエリーセットを取得したように見えました。 –

+0

'intersection = Model.objects.filter(...)&Model.objects.filter(...)'そして 'return HttpResponse("%s "%intersection.query)'これはできますか? Djangoが2つのクエリを1つにまとめるときにDjangoが何をしているのかを簡単に把握できるようにします。 –

+0

これは問題ありませんが、ユニークな行を取得できませんでした。 –

2

あなたはPythonではなく、データベースにそれをしたい場合:

intersection = set(queryset1) & set(queryset2) 

問題追加した注釈にqueriesdueで別の注釈を使用している場合、オブジェクトが異なって見えるかもしれないということです...

0

あなたは本当にただ、その後、カウントがゼロであるか否かに基づいてフィルタリングするこのshoulを注釈を使用している場合dの代わりに作業:

class FeatureManager(models.Manager): 

    def without_test_cases(self): 
     return self.get_query_set().filter(testcase__pk__isnull=True) 

    def standardised(self): 
     return self.get_query_set().filter(documentation_set__standard__isnull=False) 

注釈が心配されなくなったので、2つのクエリは非常にスムーズに交差する必要があります。

+0

ああ、私を参照してください'standardised'のためのクエリが動作するとは思わないでください。これは*スタンダードではない*関連するドキュメンテーションを持つフィーチャを選択します*スタンダードである*関連ドキュメント*を持つフィーチャを選択する必要があります。 –

4

qs1.filter(pk__in = qs2)は(通常)動作するはずです。それは私のための同様の場合のために働くように思えます、それは動作し、生成されたクエリが正気に見える意味があります。 (あなたのクエリーセットの1つが値()を使ってプライマリキーの列や何か奇妙なものを選択しない場合、私はそれが壊れると信じることができます...)あなたはちょうどこのような何かを行うことができ

19

intersection = queryset1 & queryset2 

は労働組合だけではDjango 1.11を1として|

+0

ありがとう、それは動作します!スライスされたクエリーセットでは機能しません –

4

&を置き換えるためには、今では機能intersection()

>>> qs1.intersection(qs2, qs3) 
関連する問題