问题:按属性筛选
是否可以通过模型属性过滤Django查询集?
我的模型中有一个方法:
@property
def myproperty(self):
[..]
现在我想按此属性进行过滤,例如:
MyModel.objects.filter(myproperty=[..])
这有可能吗?
回答 0
不。Django过滤器在数据库级别运行,生成SQL。要基于Python属性进行过滤,您必须将对象加载到Python中以评估该属性-到那时,您已经完成了加载该对象的所有工作。
回答 1
我可能会误解您的原始问题,但是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())
回答 2
摆脱@TheGrimmScientist建议的解决方法,您可以通过在Manager或QuerySet上定义这些“ sql属性”,然后重新使用/链接/组成它们,来制成这些“ 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)
使用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/。请注意,我将关闭文档,并且尚未测试以上内容。
回答 3
看起来将F()与批注一起使用将是我的解决方案。
它不会被过滤@property
,因为F
在将对象带入python之前会与数据库进行对话。但是仍然把它作为答案,因为我想要按属性过滤的原因实际上是希望通过对两个不同字段进行简单算术运算的结果来过滤对象。
因此,类似以下内容:
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)
我知道这不是最有效的解决方案,但在像我这样的简单情况下可能会有所帮助
回答 5
请有人纠正我,但我想至少在我自己的情况下,我已经找到了解决方案。
我想处理所有属性完全等于…的元素。
但是我有几个模型,这个例程应该适用于所有模型。它确实:
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”(属性名称,属性值)组合的字典完全相等的所有元素。
第一个参数采用(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
和?你怎么看?
回答 6
我知道这是一个古老的问题,但是对于那些跳到这里的人来说,我认为阅读下面的问题和相对答案很有用:
回答 7
它也可能通过使用查询集批注复制属性get / set-逻辑,建议如@rattray和@thegrimmscientist,会同的property
。这可能会产生在Python级别和数据库级别都可以使用的东西。
但是,不确定其缺点:请参阅此SO问题作为示例。