是否可以按模型属性过滤Django查询集?
我的模型中有一个方法:
@property
def myproperty(self):
[..]
现在我希望按以下属性进行过滤:
MyModel.objects.filter(myproperty=[..])
这有可能吗?
答案 0 :(得分:63)
不。 Django过滤器在数据库级别运行,生成SQL。要根据Python属性进行过滤,您必须将对象加载到Python中以评估属性 - 此时,您已经完成了加载它的所有工作。
答案 1 :(得分:33)
我可能会误解你原来的问题,但是在python中有一个filter内置。
filtered = filter(myproperty, MyModel.objects)
但最好使用list comprehension:
filtered = [x for x in MyModel.objects if x.myproperty()]
甚至更好,generator expression:
filtered = (x for x in MyModel.objects if x.myproperty())
答案 2 :(得分:12)
看起来using F() with annotations将是我的解决方案。
它不会按@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
然后对所有对象进行列表理解。
答案 3 :(得分:11)
重新关注@ 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/。 请注意,我将删除文档并且未对上述内容进行测试。
答案 4 :(得分: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),
第二个字典如下: {" 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
和?你觉得怎么样?
答案 5 :(得分:1)
我知道这是一个老问题,但为了跳到这里,我认为阅读下面的问题和相关答案是有用的:
答案 6 :(得分:1)
我遇到了同样的问题,并且我开发了这个简单的解决方案:
objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
MyModel.objects.filter(id__in=objects_id)
我知道这不是性能最高的解决方案,但在像我这样的简单情况下可能会有所帮助
答案 7 :(得分:0)
也可能使用查询集注释来复制属性get / set-logic,如建议的那样。由@rattray和@thegrimmscientist组成,与一起property
。这可能会产生在Python级别和在数据库级别均适用的功能。
不确定这些缺点,例如:请参见this SO question。