按属性过滤
是否有可能按属性过滤?
我在我的模型中有一个方法:
@property def myproperty(self): [..]
现在我想过滤这个属性,如:
MyModel.objects.filter(myproperty=[..])
这是不是有可能?
不。 Djangofilter在数据库级运行,生成SQL。 要基于Python属性进行过滤,您必须将对象加载到Python中才能评估属性 – 此时,您已经完成了所有加载工作。
我可能会误解你的原始问题,但python中有一个内置的filter 。
filtered = filter(myproperty, MyModel.objects)
但是使用列表理解更好:
filtered = [x for x in MyModel.objects if x.myproperty()]
甚至更好,一个生成器expression式 :
filtered = (x for x in MyModel.objects if x.myproperty())
看起来像使用F()注释将是我的解决scheme。
它不会被@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
然后对所有对象进行列表理解。
请有人纠正我,但我想我已经find了一个解决scheme,至less对我自己的情况。
我想要处理所有这些元素的属性完全等于…无论如何。
但是我有几个模型,这个例程应该适用于所有模型。 它确实:
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)
有了这个通用的子程序,我可以select所有那些完全等于“指定”(propertyname,propertyvalue)组合字典的元素。
第一个参数需要一个(models.Model),
第二个字典如:{“property1”:“77”,“property2”:“12”}
它创build一个像SQL一样的SQL语句
SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'
并在这些元素上返回一个QuerySet。
这是一个testingfunction:
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
和? 你怎么看?
对TheGrimmScientistbuild议的解决方法进行抨击,可以通过在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)
使用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/ 。 请注意,我正在closures文档,并没有testing以上。
我知道这是一个古老的问题,但为了那些跳在这里我认为这是有益的阅读下面的问题和相对的答案:
如何在Django 1.4中自定义pipe理filter