【发布时间】: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')
【问题讨论】: