【问题标题】:Django odd behaviour with Q objects and `and_` (vs `and`) [duplicate]Q对象和`and_`(与`and`)的Django奇怪行为[重复]
【发布时间】:2019-09-17 10:52:15
【问题描述】:

我一直在尝试根据一些函数参数动态构造过滤器列表。 我使用Q 对象构建了一个谓词列表,最后我构造了一个谓词:

filters = []
... some code appending Q objects to filters ...
combined_filter = reduce(and_, filters)

然后我查询我的数据库对象:

MyModel.objects.filter(combined_filter)

当我尝试将 Q 对象与 operator.and_ 组合时,我注意到了一些奇怪的行为。

例如比较输出:

items = Item.objects.filter(and_(is_metal, ~is_wood))
print([i.name for i in items])

items = Item.objects.filter(is_metal and ~is_wood)
print([i.name for i in items])

items = Item.objects.filter(is_metal, ~is_wood)
print([i.name for i in items])

我明白了:

['Table', 'Container']
['Container']
['Table', 'Container']

and_and 之间行为不同的原因是什么?

我的预期输出是只得到['Container'](完整示例见下文,“容器”是唯一只有“金属”作为材料的东西,“桌子”应该被排除在外,因为它也有“木头”)。

后续问题是:使用reduce 时如何获得and 的行为?

我的 django 版本是 2.0.7 我已经在https://repl.it/repls/AgonizingBossyEfficiency 上重现了这个确切的问题

如果上面的链接失效,我修改的所有代码如下:

models.py

from django.db import models

class Material(models.Model):
  name = models.CharField(max_length=50)

class Item(models.Model):
  name = models.CharField(max_length=50)
  materials = models.ManyToManyField(Material)

views.py

from django.shortcuts import render
from django.db import transaction
from django.db.models import Q
from operator import and_
from .models import Material, Item

# Create your views here.
def home(request):
    with transaction.atomic():
      metal = Material(name="metal")
      metal.save()
      wood = Material(name="wood")
      wood.save()

      table = Item(name="Table")
      table.save()
      table.materials.add(metal, wood)
      table.save()

      chair = Item(name="Chair")
      chair.save()
      chair.materials.add(wood)
      chair.save()

      container = Item(name="Container")
      container.save()
      container.materials.add(metal)
      container.save()

      is_metal = Q(materials__name__in = ('metal',))
      is_wood = Q(materials__name__in = ('wood',))

      items = Item.objects.filter(and_(is_metal, ~is_wood))
      print([i.name for i in items])

      items = Item.objects.filter(is_metal and ~is_wood)
      print([i.name for i in items])

      items = Item.objects.filter(is_metal, ~is_wood)
      print([i.name for i in items])

      raise Exception('nope')
    return render(request, 'main/index.html')

【问题讨论】:

标签: python django


【解决方案1】:

正如 Ilja 所指出的,我的问题实际上是重复的。

我的主要问题是认为operator.and_ 应用逻辑and。 然而事实并非如此,operator.and_ 是位运算符。

如果我比较 operator.and_(is_metal, is_wood)is_metal & is_wood(而不是 is_metal && is_wood == is_metal and is_wood)的行为,我会得到相同的结果。

所以我的问题是我使用的是按位和我想要的逻辑与。

【讨论】:

  • 小心点。组合 Q 对象的documented way 是按位和,即&_and,与您正确得出的结论相同。但是,如标记为重复的问题中所述,当使用and 时,您最终只会得到最后一个 Q 对象,即filter(is_metal and ~is_wood) 等效于filter(~is_wood) 甚至filter('hi there' and ~is_wood)。这几乎不是你想要的。
  • 错误的and 得到预期结果,而正确的_and 得到意外结果的原因在于为 x- 的多面正确定义多标准过滤器的复杂性多对多关系——这就是你应该成为的lookinginto
  • 感谢@EndreBoth。两个 cmets 都很有用。经过一番思考,我首先自己解决了问题,然后我意识到了多对多查询的问题。我仍然觉得filterQ.__and__ 很混乱。
猜你喜欢
  • 2011-02-12
  • 2021-12-13
相关资源
最近更新 更多