web-dev-qa-db-ja.com

プロパティでフィルター

モデルプロパティでDjangoクエリセットをフィルタリングできますか?

私のモデルにメソッドがあります:

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

そして今、私は次のようなこのプロパティでフィルタリングしたい:

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

これはどういうわけか可能ですか?

76
schneck

いや。 Djangoフィルターはデータベースレベルで動作し、SQLを生成します。Pythonプロパティに基づいてフィルターするには、オブジェクトをPythonプロパティを評価します。その時点で、ロードするためのすべての作業をすでに完了しています。

66
Glenn Maynard

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

filtered = filter(myproperty, MyModel.objects)

しかし、 リスト内包表記 を使用する方が適切です:

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

さらに良いことに、 ジェネレーター式

filtered = (x for x in MyModel.objects if x.myproperty())
36
Clint

F()アノテーション付き を使用すると、これに対する解決策になります。

FはPythonにオブジェクトが取り込まれる前にデータベースと通信するため、@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

次に、すべてのオブジェクトにわたってリストを理解します。

13

@TheGrimmScientistの推奨される回避策を取り除くと、これらの「sqlプロパティ」をManagerまたはQuerySetで定義し、再利用/チェーン/構成することで作成できます。

マネージャーと:

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)

QuerySetを使用する場合:

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/ を参照してください。私はドキュメントをオフにするつもりであり、上記をテストしていないことに注意してください。

11
rattray

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

プロパティがまったく同じであるすべての要素で作業したい...何でも。

しかし、私にはいくつかのモデルがあり、このルーチンはすべてのモデルで機能するはずです。そしてそれは:

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)

この汎用サブルーチンを使用すると、「specify」(propertyname、propertyvalue)の組み合わせの辞書に正確に一致するすべての要素を選択できます。

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

次のような辞書:{"property1": "77"、 "property2": "12"}

そして、次のようなSQLステートメントを作成します

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

これらの要素の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 

そして?どう思いますか?

3
akrueger

私はそれが古い質問であることを知っていますが、ここにジャンプする人たちのために、以下の質問と相対的な答えを読むことが役に立つと思います:

Django 1.4 で管理フィルターをカスタマイズする方法

1
FSp

提案されているように、get/set-logicプロパティを複製するクエリセット注釈を使用することも可能です。 @ rattray および @ thegrimmscientistpropertyの組み合わせ。これにより、Pythonレベルand)とデータベースレベルの両方で機能するものが得られる可能性があります。

ただし、欠点についてはわかりません。例については this SO question を参照してください。

0
djvg