按属性筛选

是否可以根据模型属性过滤 Django 查询集?

在我的模型中有一个方法:

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

现在我想通过这个属性进行过滤,比如:

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

这有可能吗?

86465 次浏览

没有。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())

我知道这是一个古老的问题,但是为了那些在这里跳跃的人,我认为阅读下面的问题和相关的答案是有用的:

如何在 Django 1.4中自定义管理过滤器

请有人纠正我,但我想我已经找到了解决办法,至少对我自己的情况。

我想研究所有的元素它们的性质都等于... 随便什么。

但是我有几个模型,这个程序应该适用于所有的模型,而且它确实适用:

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)

通过这个通用子例程,我可以选择所有那些完全等同于字典中的“指定”(property tyname,property tyvalue)组合的元素。

第一个参数接受一个(models.Model) ,

第二个词典喜欢: {“ property ty1”: “77”,“ property ty2”: “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

然后呢,你觉得怎么样?

看来 使用带注释的 F ()就是我的解决方案了。

它不会通过 @property进行过滤,因为在将对象引入 python 之前,F会与数据库进行对话。但是仍然把它作为一个答案放在这里,因为我想要按属性过滤的原因实际上是想要通过两个不同字段的简单算术结果来过滤对象。

那么,大概是这样的:

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

然后在所有物体上做一个列表内涵。

根据@TheGrimScientist 建议的解决方案,您可以通过在 Manager 或 QuerySet 上定义这些“ sql 属性”,并重用/链接/组合它们:

与经理:

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/。 请注意,我正在离开文档,并且还没有测试上面的内容。

也可以使用查询集注释来复制属性 get/set-Logic,如 @ rattray@ 冷酷的科学家与... 结合在一起property所建议的那样。这可能会产生一些在 Python 级别 还有和数据库级别都可以工作的东西。

但是,不确定这些缺点: 请参见 这个所以问题的示例。

我也有同样的问题,我想出了一个简单的解决办法:

objects = [
my_object
for my_object in MyModel.objects.all()
if my_object.myProperty == [...]
]

这不是一个性能解决方案,不应该在包含大量数据的表中进行。这对于一个简单的解决方案或者一个个人的小项目来说是非常好的。