按属性过滤

sch*_*eck 81 python django orm

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

我的模型中有一个方法:

@property
def myproperty(self):
    [..]
Run Code Online (Sandbox Code Playgroud)

现在我想通过这个属性过滤,如:

MyModel.objects.filter(myproperty=[..])
Run Code Online (Sandbox Code Playgroud)

这有点可能吗?

Gle*_*ard 68

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

  • 运气不好,这个功能没有实现,将是一个有趣的扩展,至少过滤掉匹配的对象_after_结果集已经构建. (3认同)

Cli*_*int 37

我可能误解了你原来的问题,但是在python中有一个内置的过滤器.

filtered = filter(myproperty, MyModel.objects)
Run Code Online (Sandbox Code Playgroud)

但最好使用列表理解:

filtered = [x for x in MyModel.objects if x.myproperty()]
Run Code Online (Sandbox Code Playgroud)

甚至更好,一个生成器表达式:

filtered = (x for x in MyModel.objects if x.myproperty())
Run Code Online (Sandbox Code Playgroud)

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

The*_*ist 13

看起来像使用注释的F()将是我的解决方案.

它不会过滤@property,因为F在对象之前与数据库的对话被带入python.但仍然把它放在这里作为答案,因为我想要按属性过滤的原因是真的想要通过两个不同字段上的简单算术结果来过滤对象.

所以,有些东西:

companies = Company.objects\
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\
    .filter(chairs_needed__lt=4)
Run Code Online (Sandbox Code Playgroud)

而不是将属性定义为:

@property
def chairs_needed(self):
    return self.num_employees - self.num_chairs
Run Code Online (Sandbox Code Playgroud)

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


rat*_*ray 12

重新关注@ 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)
Run Code Online (Sandbox Code Playgroud)

使用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()
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅https://docs.djangoproject.com/en/1.9/topics/db/managers/.请注意,我将删除文档并且未对上述内容进行测试.


Vit*_*ate 8

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

objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
MyModel.objects.filter(id__in=objects_id)
Run Code Online (Sandbox Code Playgroud)

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

  • 不幸的是,这会给数据库和内存带来相当重的负载,因为您首先查询所有对象,然后进行列表理解。一旦您在具有数百万行的实际生产环境中运行它,这将很快开始杀死您的数据库。 (4认同)