加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

django – 在qs.excude()中通过span关系评估同一个对象

发布时间:2020-12-20 13:46:31 所属栏目:Python 来源:网络整理
导读:考虑一个带房间对象的酒店和这些房间的预订对象.我想找到哪个房间在特定时期可用,或者(从下面的例子中),从哪个日期开始. 可以“删除”预约,这可以通过设置“实时”字段获得.所以它们实际上并没有删除,只是处于非活动状态,这需要保持这种状态. indate = "2014
考虑一个带房间对象的酒店和这些房间的预订对象.我想找到哪个房间在特定时期可用,或者(从下面的例子中),从哪个日期开始.

可以“删除”预约,这可以通过设置“实时”字段获得.所以它们实际上并没有删除,只是处于非活动状态,这需要保持这种状态.

>>> 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)无济于事.
请注意,上面只是一个更大的查询中的代码提取(问题所在的位置).因此我不能简单地做qs.filter(reservation__live = True).移动查询以过滤而不是排除似乎不是一个选项.

编辑:根据请求添加我的简化模型,如下所示.
Edit2:添加一些解释问题的测试数据,并根据@ knbk的建议添加一个链式exclude().

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>))

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读