2009-07-30 10 views
56

プロパティでフィルタリングすることはできますか?プロパティでフィルタ

@property 
def myproperty(self): 
    [..] 

を、今私のような、このプロパティによってフィルタする:

私は私のモデルにメソッドを持っている、これは何とか可能です

MyModel.objects.filter(myproperty=[..]) 

+0

これはSQLAlchemyにあります:http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html djangoはSQLAlchemyとhttps://pypi.python.org/pypi/aldjemyで接続できますしかし、私は2つがあなたが望むように接続できるかどうか疑問です。 – rattray

答えて

47

いいえ。 Djangoフィルタはデータベースレベルで動作し、SQLを生成します。 Pythonのプロパティに基づいてフィルタを適用するには、そのオブジェクトをPythonにロードしてプロパティを評価する必要があります。その時点で、すでにロードを完了しています。

+2

この機能が実装されていないという不運があっても、結果セットがビルドされた後、少なくとも一致するオブジェクトをフィルタリングするという面白い拡張機能になります。 – schneck

+0

管理者はどのように対処するのですか?いくつかの回避策がありますか? – andi

30

私は元の質問を誤解しているかもしれませんが、filterがPythonに組み込まれています。

filtered = filter(myproperty, MyModel.objects) 

しかし、それは使用することをお勧めしますlist comprehension

filtered = [x for x in MyModel.objects if x.myproperty()] 

またはより良い、generator expression

filtered = (x for x in MyModel.objects if x.myproperty()) 
+8

これはPythonオブジェクトを持っていればそれをフィルタリングしますが、SQLクエリを構築するDjango QuerySet.filterについて質問しています。 –

+0

右ですが、上記で説明したように、私は自分のデータベースフィルタにプロパティを追加したいと思います。クエリが完了した後のフィルタリングは、まさに私が避けたいものです。 – schneck

+0

すごく、ありがとう! –

3

誰かが私を訂正してください。少なくとも、自分の場合は解決策を見つけたと思います。

私は、プロパティが...と何か等しいすべての要素に取り組みたいと思います。

私はいくつかのモデルを持っていますが、このルーチンはすべてのモデルで機能するはずです。そして、それがない:

def selectByProperties(modelType, specify): 
    clause = "SELECT * from %s" % modelType._meta.db_table 

    if len(specify) > 0: 
     clause += " WHERE " 
     for field, eqvalue in specify.items(): 
      clause += "%s = '%s' AND " % (field, eqvalue) 
     clause = clause [:-5] # remove last AND 

    print clause 
    return modelType.objects.raw(clause) 

この普遍的なサブルーチンで、私は正確に「を指定」(propertyNameの、PropertyValueを)組み合わせの私の辞書に等しく、これらすべての要素を選択することができます。

最初のパラメータは、(models.Model)かかり、

第辞書等: { "プロパティ1": "77"、 "property2": "12"}

そしてそれは、作成

SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12' 

のようなSQL文を返し、これらの要素に対してQuerySetを返します。

これは、テスト関数である:

from myApp.models import myModel 

def testSelectByProperties(): 

    specify = {"property1" : "77" , "property2" : "12"} 
    subset = selectByProperties(myModel, specify) 

    nameField = "property0" 
    ## checking if that is what I expected: 
    for i in subset: 
     print i.__dict__[nameField], 
     for j in specify.keys(): 
      print i.__dict__[j], 
     print 

と?どう思いますか?

7

using F() with annotationsのように見えます。これは私の解決策です。

FはオブジェクトがPythonに持ち込まれる前に、Fと会話するので、それは@propertyでフィルタリングされません。しかし、まだそれを答えとして置くのは、プロパティでフィルタを欲しいという私の理由は、2つの異なるフィールドの単純な算術の結果によってオブジェクトをフィルタリングしたいということでした。

ので、の線に沿って何か:かなりするプロパティを定義するよりも

companies = Company.objects\ 
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\ 
    .filter(chairs_needed__lt=4) 

@property 
def chairs_needed(self): 
    return self.num_employees - self.num_chairs 

その後、すべてのオブジェクト間でリストの内包を行います。

3

TheGrimmScientistの解決策として、@オフリフ、あなたがマネージャまたはクエリセットにそれらを定義することで、これらの「SQLプロパティ」を作ることができ、かつ再利用/チェーン/それらを構成する:

Managerでは:

class CompanyManager(models.Manager): 
    def with_chairs_needed(self): 
     return self.annotate(chairs_needed=F('num_employees') - F('num_chairs')) 

class Company(models.Model): 
    # ... 
    objects = CompanyManager() 

Company.objects.with_chairs_needed().filter(chairs_needed__lt=4) 
クエリセットで

class CompanyQuerySet(models.QuerySet): 
    def many_employees(self, n=50): 
     return self.filter(num_employees__gte=n) 

    def needs_fewer_chairs_than(self, n=5): 
     return self.with_chairs_needed().filter(chairs_needed__lt=n) 

    def with_chairs_needed(self): 
     return self.annotate(chairs_needed=F('num_employees') - F('num_chairs')) 

class Company(models.Model): 
    # ... 
    objects = CompanyQuerySet.as_manager() 

Company.objects.needs_fewer_chairs_than(4).many_employees() 

は、より多くのためにhttps://docs.djangoproject.com/en/1.9/topics/db/managers/を参照してください。 私はドキュメンテーションから外しており、上記をテストしていないことに注意してください。

関連する問題