【问题标题】:Filter by property按属性过滤
【发布时间】:2020-08-03 05:10:04
【问题描述】:

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

我的模型中有一个方法:

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

现在我想按此属性进行过滤,例如:

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

这有可能吗?

【问题讨论】:

标签: python django orm


【解决方案1】:

不。 Django 过滤器在数据库级别运行,生成 SQL。要根据 Python 属性进行过滤,您必须将对象加载到 Python 中以评估属性——此时,您已经完成了加载它的所有工作。

【讨论】:

  • 不幸的是,这个功能没有实现,这将是一个有趣的扩展,至少在构建结果集之后 过滤掉匹配的对象。
  • 如何在admin中处理?有什么解决方法吗?
【解决方案2】:

我可能误解了你原来的问题,但是 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())

【讨论】:

  • 一旦你有一个 Python 对象就可以过滤它,但他问的是 Django QuerySet.filter,它构造 SQL 查询。
  • 对,但如上所述,我想将该属性添加到我的数据库过滤器中。查询完成后过滤正是我想要避免的。
  • x.myproperty 不带括号
  • 但是它给了你一个 python 列表,而不是一个 django 查询集
【解决方案3】:

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

How to customize admin filter in Django 1.4

【讨论】:

  • 对于那些略读这个答案的人 - 这个链接是关于在 Django Admin 中使用“SimpleListFilter”实现列表过滤器的信息。有用,但不是问题的答案,除非在非常具体的情况下。
【解决方案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)

使用这个通用子例程,我可以选择所有与我的“指定”(属性名、属性值)组合字典完全相同的元素。

第一个参数取一个(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 

然后呢?你怎么看?

【讨论】:

  • 一般来说,这似乎是一个体面的工作。我不会说它是理想的,但是每次你需要这样的东西时,它都比必须派生一个存储库来修改你从 PyPI 安装的包的模型要好。
  • 现在我有时间玩一下它:这种方法的真正缺点是 .raw() 返回的查询集不是成熟的查询集,我的意思是那里是缺少的查询集方法:AttributeError: 'RawQuerySet' object has no attribute 'values'
【解决方案5】:

看起来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

然后对所有对象进行列表推导。

【讨论】:

    【解决方案6】:

    借鉴@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/ 了解更多信息。 请注意,我将关闭文档并且尚未测试上述内容。

    【讨论】:

      【解决方案7】:

      也可以使用复制属性 get/set-logic 的查询集注释,如建议的那样,例如@rattray@thegrimmscientist结合 property。这可能会产生在 Python 级别 数据库级别都有效的东西。

      但不确定其缺点:请参阅 this SO question 示例。

      【讨论】:

      • 您的代码审查问题链接通知它已被其作者自愿删除。您是否介意在此处更新您的答案,或者使用代码链接或解释,或者只是删除您的答案?
      • @hlongmore:很抱歉。该问题已移至SO。我修复了上面的链接。
      【解决方案8】:

      我遇到了同样的问题,我开发了这个简单的解决方案:

      objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
      MyModel.objects.filter(id__in=objects_id)
      

      我知道这不是最高效的解决方案,但在我的简单情况下可能会有所帮助

      【讨论】:

        猜你喜欢
        • 2013-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-31
        相关资源
        最近更新 更多