按属性过滤
- 2025-02-10 08:57:00
- admin 原创
- 55
问题描述:
是否可以通过模型属性过滤 Django 查询集?
我的模型中有一个方法:
@property
def myproperty(self):
[..]
现在我想通过这个属性进行过滤,例如:
MyModel.objects.filter(myproperty=[..])
这可能吗?
解决方案 1:
不是。Django 过滤器在数据库级别运行,生成 SQL。要根据 Python 属性进行过滤,您必须将对象加载到 Python 中以评估该属性——此时,您已经完成了加载它的所有工作。
解决方案 2:
我可能误解了你最初的问题,但是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())
解决方案 3:
借鉴@TheGrimmScientist 建议的解决方法,您可以通过在 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/。请注意,我没有参考文档,也没有测试上述内容。
解决方案 4:
看起来使用带有注释的 F()将是我的解决方案。
它不会按 进行过滤@property
,因为F
在将对象带入 python 之前会与数据库进行对话。但仍然将其作为答案放在这里,因为我想要按属性进行过滤的原因实际上是想根据两个不同字段的简单算术结果来过滤对象。
因此,大致如下:
companies = Company.objects\n .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\n .filter(chairs_needed__lt=4)
而不是将属性定义为:
@property
def chairs_needed(self):
return self.num_employees - self.num_chairs
然后对所有对象进行列表理解。
解决方案 5:
我遇到了同样的问题,并且我开发了这个简单的解决方案:
objects = [
my_object
for my_object in MyModel.objects.all()
if my_object.myProperty == [...]
]
这不是一个高性能的解决方案,它不应该在包含大量数据的表中完成。这对于简单的解决方案或个人小项目来说很棒。
解决方案 6:
请有人纠正我,但我想我已经找到了解决方案,至少对我自己的情况而言。
我想要研究所有那些属性完全等于...无论什么的元素。
但是我有多个模型,这个例程应该适用于所有模型。并且它确实如此:
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)
使用这个通用子程序,我可以选择所有与我的“指定”(属性名称,属性值)组合词典完全相等的元素。
第一个参数采用(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
然后呢?你觉得怎么样?
解决方案 7:
我知道这是一个老问题,但是为了那些跳到这里的人,我认为阅读下面的问题和相关答案是有用的:
如何在 Django 1.4 中自定义管理过滤器
解决方案 8:
还可以使用重复属性 get/set-logic 的查询集注释,例如@rattray和@thegrimmscientist所建议的,与 结合使用。property
这可以产生在 Python 级别和数据库级别都有效的东西。
但不确定其缺点:请参阅这个 SO 问题作为示例。