问题:按属性筛选

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

我的模型中有一个方法:

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

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

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

这有可能吗?

Is it possible to filter a Django queryset by model property?

i have a method in my model:

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

and now i want to filter by this property like:

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

is this somehow possible?


回答 0

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

Nope. Django filters operate at the database level, generating SQL. To filter based on Python properties, you have to load the object into Python to evaluate the property–and at that point, you’ve already done all the work to load it.


回答 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())

I might be misunderstanding your original question, but there is a filter builtin in python.

filtered = filter(myproperty, MyModel.objects)

But it’s better to use a list comprehension:

filtered = [x for x in MyModel.objects if x.myproperty()]

or even better, a generator expression:

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/。请注意,我将关闭文档,并且尚未测试以上内容。

Riffing off @TheGrimmScientist’s suggested workaround, you can make these “sql properties” by defining them on the Manager or the QuerySet, and reuse/chain/compose them:

With a Manager:

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)

With a 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()

See https://docs.djangoproject.com/en/1.9/topics/db/managers/ for more. Note that I am going off the documentation and have not tested the above.


回答 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

然后对所有对象进行列表理解。

Looks like using F() with annotations will be my solution to this.

It’s not going to filter by @property, since F talks to the databse before objects are brought into python. But still putting it here as an answer since my reason for wanting filter by property was really wanting to filter objects by the result of simple arithmetic on two different fields.

so, something along the lines of:

companies = Company.objects\
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\
    .filter(chairs_needed__lt=4)

rather than defining the property to be:

@property
def chairs_needed(self):
    return self.num_employees - self.num_chairs

then doing a list comprehension across all objects.


回答 4

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

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

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

I had the same problem, and I developed this simple solution:

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

I know it’s not the most performatic solution, but may help in simple cases as mine


回答 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 

和?你怎么看?

PLEASE someone correct me, but I guess I have found a solution, at least for my own case.

I want to work on all those elements whose properties are exactly equal to … whatever.

But I have several models, and this routine should work for all models. And it does:

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)

With this universal subroutine, I can select all those elements which exactly equal my dictionary of ‘specify’ (propertyname,propertyvalue) combinations.

The first parameter takes a (models.Model),

the second a dictionary like: {“property1” : “77” , “property2” : “12”}

And it creates an SQL statement like

SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'

and returns a QuerySet on those elements.

This is a test function:

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 

And? What do you think?


回答 6

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

如何在Django 1.4中自定义管理过滤器

i know it is an old question, but for the sake of those jumping here i think it is useful to read the question below and the relative answer:

How to customize admin filter in Django 1.4


回答 7

它也可能通过使用查询集批注复制属性get / set-逻辑,建议如@rattray@thegrimmscientist会同property。这可能会产生在Python级别数据库级别都可以使用的东西。

但是,不确定其缺点:请参阅此SO问题作为示例。

It may also be possible to use queryset annotations that duplicate the property get/set-logic, as suggested e.g. by @rattray and @thegrimmscientist, in conjunction with the property. This could yield something that works both on the Python level and on the database level.

Not sure about the drawbacks, however: see this SO question for an example.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。