プロパティでフィルター


95

モデルのプロパティでDjangoクエリセットをフィルタリングすることは可能ですか?

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

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

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

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

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


:これはSQLAlchemyのであるdocs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html、あなたは経由SQLAlchemyのでジャンゴ接続することができますpypi.python.org/pypi/aldjemy 2を接続することができることが、私は疑わしいですあなたが彼らになりたい方法。
2016年

回答:


78

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


4
この機能が実装されていないという不運は、結果セットがビルドされた後に少なくとも一致するオブジェクト除外する興味深い拡張機能です。
シュネック、2009

1
管理者でそれを処理する方法?回避策はありますか?
アンディラブス2014

39

元の質問を誤解しているかもしれませんが、Pythonに組み込まれているフィルターがあります。

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())

14
これは、Pythonオブジェクトを取得するとフィルタリングするように機能しますが、SQLクエリを構築するDjango QuerySet.filterについて質問しています。
グレンメイナード

1
そうですが、上で説明したように、データベースフィルターにプロパティを追加したいと思います。クエリが完了した後のフィルタリングは、まさに避けたいものです。
シュネック2009

18

@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/を参照してください。私はドキュメントから離れて、上記をテストしていません。


14

注釈でF()を使用するように見えますが、これに対する私の解決策になります。

によってフィルタリングするつもりはない@propertyことから、FオブジェクトはPythonに持ち込まれる前にdatabseに会談。しかし、プロパティによるフィルターが必要な私の理由は、実際には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

次に、すべてのオブジェクトにわたってリスト内包を行います。


4

私は同じ問題を抱えていました、そして私はこの簡単な解決策を開発しました:

objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
MyModel.objects.filter(id__in=objects_id)

私はそれが最もパフォーマンスの高い解決策ではないことを知っていますが、私のような単純なケースでは役立つかもしれません


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)を受け取り、

2番目は次のような辞書です:{"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 

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


一般的にそれはまともな回避策のようです。理想的とは言えませんが、このようなものが必要になるたびに、リポジトリをフォークしてPyPIからインストールしたパッケージのモデルを変更する必要はありません。
hlongmore

そして今、私はそれで少し遊ぶ時間を持っていたこと:このアプローチの本当の欠点は、私が欠落しているクエリセットメソッドがある意味これにより本格的なクエリセット、ではないた.raw()によって返されるクエリセットであります:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore

1

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

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


1
この答えをざっと見ている人のために-このリンクは、「SimpleListFilter」を使用してDjango Adminにリストフィルターを実装することに関する情報です。非常に特殊な場合を除いて、有用ですが、質問に対する回答ではありません。
jenniwren 2018年

0

またによって提案された例として、プロパティのget /セットロジックを複製するクエリセットの注釈を、使用することも可能である@rattray@thegrimmscientistと一緒にproperty。これにより、Pythonレベルデータベースレベルの両方で機能するものが生成されます。

ただし、欠点については不明です。例については、このSOの質問を参照してください。


コードレビューの質問リンクは、作成者によって自発的に削除されたことを通知します。コードへのリンクまたは説明を使用して、ここで回答を更新しますか、それとも単に回答を削除しますか?
hlongmore

@hlongmore:すみません。その質問はSOに移されました。上記のリンクを修正しました。
djvg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.