モデルのプロパティでDjangoクエリセットをフィルタリングすることは可能ですか?
私のモデルにはメソッドがあります:
@property
def myproperty(self):
[..]
そして今、私はこのプロパティでフィルタリングしたい:
MyModel.objects.filter(myproperty=[..])
これはどういうわけか可能ですか?
モデルのプロパティでDjangoクエリセットをフィルタリングすることは可能ですか?
私のモデルにはメソッドがあります:
@property
def myproperty(self):
[..]
そして今、私はこのプロパティでフィルタリングしたい:
MyModel.objects.filter(myproperty=[..])
これはどういうわけか可能ですか?
回答:
いいえ。Djangoフィルターはデータベースレベルで動作し、SQLを生成します。Pythonプロパティに基づいてフィルタリングするには、オブジェクトをPythonにロードしてプロパティを評価する必要があります。その時点で、プロパティをロードするための作業はすべて完了しています。
元の質問を誤解しているかもしれませんが、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())
@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/を参照してください。私はドキュメントから離れて、上記をテストしていません。
注釈で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
次に、すべてのオブジェクトにわたってリスト内包を行います。
誰かが私を訂正してください、しかし私は少なくとも私自身の場合のための解決策を見つけたと思います。
プロパティが完全に等しいすべての要素に取り組みたい...何でも。
しかし、私にはいくつかのモデルがあり、このルーチンはすべてのモデルで機能するはずです。そしてそれはします:
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
そして?どう思いますか?
AttributeError: 'RawQuerySet' object has no attribute 'values'
私はそれが古い質問であることを知っていますが、ここにジャンプする人たちのために、私は以下の質問と相対的な答えを読むことは有用だと思います:
またによって提案された例として、プロパティのget /セットロジックを複製するクエリセットの注釈を、使用することも可能である@rattrayと@thegrimmscientist、と一緒にproperty
。これにより、Pythonレベルとデータベースレベルの両方で機能するものが生成されます。