django – 在qs.excude()中通过span关系评估同一个对象
考虑一个带房间对象的酒店和这些房间的预订对象.我想找到哪个房间在特定时期可用,或者(从下面的例子中),从哪个日期开始.
可以“删除”预约,这可以通过设置“实时”字段获得.所以它们实际上并没有删除,只是处于非活动状态,这需要保持这种状态. >>> indate = "20141225" >>> Room.objects.exclude( (models.Q(reservation__live=True,reservation__date_out__gt=indate) | models.Q(reservation__date_out__isnull=True,reservation__live=True)) ) 问题陈述:上面的代码有一个令人遗憾的副作用:当有一个生命值= True的预订超出期限,并且当同一个房间的期间内有另一个预约live = False时,那个房间将被排除在外.情况并非如此:由于我要求的期间内的预订未设置为live = True,因此不应将其考虑在内. 看起来我上面的查询在通过房间预订关系进行(live = True和date_out__gt = indate)比较时没有考虑相同的预留. 问题:在exclude()中是否有一种方法可以确保在比较中考虑相同的保留? 我试着玩负模型.Q(~Models.Q)无济于事. 编辑:根据请求添加我的简化模型,如下所示. class AvailableRoomManager(models.Manager): def available_with_Q(self,indate,outdate): qs = super(AvailableRoomManager,self).get_queryset() if indate and outdate: qs = qs.exclude(models.Q(reservation__date_out__gt=indate,reservation__date_in__lt=outdate,reservation__live=True) | models.Q(reservation__date_out__isnull=True,reservation__date_in__isnull=False,reservation__live=True)) elif indate and not outdate: qs = qs.exclude((models.Q(reservation__date_out__gt=indate,reservation__live=True))) return qs def available_with_chained_excludes(self,self).get_queryset() if indate and outdate: qs = qs.exclude(reservation__date_out__gt=indate,reservation__live=True) .exclude(reservation__date_out__isnull=True,reservation__live=True) elif indate and not outdate: qs = qs.exclude(reservation__date_out__gt=indate,reservation__live=True) return qs class Room(models.Model): name = models.CharField(max_length=30,unique=True) objects = models.Manager() available_rooms = AvailableRoomManager() def __str__(self): return self.name class Reservation(models.Model): date_in = models.DateField() date_out = models.DateField(blank=True,null=True) room = models.ForeignKey(Room) live = LiveField() # See django-livefield; to do deletion. Basically adds a field "live" to the model. objects = LiveManager() all_objects = LiveManager(include_soft_deleted=True) 当搜索周期之外存在活动(live = True)时,以及在我正在搜索的时间段内存在非活动(live!= True)时,上述exclude()语句中会弹出该问题. 一些简单的测试数据使用上述模型,显示问题所在: # Let's make two rooms,R001 and R002 >>> room1 = Room.objects.get_or_create(name="R001")[0] >>> room2 = Room.objects.get_or_create(name="R002")[0] # First reservation,with no date_out,is created but then "deleted" by setting field 'live' to False >>> res1 = Reservation.objects.get_or_create(date_in="2014-12-01",date_out=None,room=room1)[0] >>> res1.live = False >>> res1.save() # Second reservation in same room is created with date_out set to Dec 15th >>> res2 = Reservation.objects.get_or_create(date_in="2014-12-01",date_out="2014-12-15",room=room1)[0] # Here I'd expect to have R001 listed as well... this is not the case >>> Room.available_rooms.available_with_Q("2014-12-16","") [<Room: R002>] >>> Room.available_rooms.available_with_chained_excludes("2014-12-16","") [<Room: R002>] # As a test,when changing "deleted" res1's Room to room2,the manager does return R001 >>> res1.room = room2 >>> res1.save() >>> Room.available_rooms.available_with_Q("2014-12-16","") [<Room: R001>,<Room: R002>] >>> Room.available_rooms.available_with_chained_excludes("2014-12-16",<Room: R002>] 解决方法
我用你的github项目测试了它,我得到了和你一样的结果.看来,当过滤器正确地将多个相关对象过滤器转换为INNER JOIN时,exclude会在某种子查询中转换它,其中每个过滤器(即使在同一个调用中)都会自行检查.
我找到了一个解决方法,那就是显式创建保留子查询: elif indate and not outdate: ress = Reservation.objects.filter(Q(live=True,date_in__isnull=False),Q(date_out__gt=indate) | Q(date_out__isnull=True)) rooms = Room.objects.exclude(reservation__in=ress) etc... 顺便说一句,如果您需要使用过滤器而不是排除,以下查询总是相同的,事实上,这就是Django在内部所做的事情: Room.objects.exclude(<some_filter>) Room.objects.filter(~Q(<some_filter>)) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |