可用项目的 Django ListView 查询集
Posted
技术标签:
【中文标题】可用项目的 Django ListView 查询集【英文标题】:Django ListView queryset for available items 【发布时间】:2019-06-01 09:39:14 【问题描述】:所以我想显示任何给定日期的所有可用项目,应该不难,但不知何故我遇到了有关相关项目的问题。
假设我们有以下模型,一个用于存储所有预订的模型和一个包含项目的模型。
然后我会创建 ListView 来检索任何给定日期之间可用的所有项目。我重写了查询集来检索用户填写的数据。
这似乎有效,但有一个问题,即使我检查“form_start_date”或“form_end_data”是否与现有预订冲突,但当单个项目有多个预订时它不起作用。
示例
项目 #01 的预订量 [X]: 2019 年 1 月 1 日至 2019 年 1 月 3 日 2019 年 1 月 11 日至 2019 年 1 月 18 日
Jan 2019 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
---------- --- --- --- --- --- --- --- --- --- ---- ---- ---- ---- ---- ---- ---- ---- ----
Item #01 X X X O O O X X X X X X X X
Item #02 X X X X X
当我检查 01-06-2019 到 01-08-2019 的可用性 [O] 时,项目 #01 不可用,我在这里缺少什么?
models.py
class Booking(models.Model):
item = models.ForeignKey('Item', on_delete=models.SET_NULL, null=True)
start_date = models.DateField()
end_date = models.DateField()
class Item(models.Model):
name = models.CharField(max_length=20, unique=True)
views.py
class AvailableItems(generic.ListView):
model = Item
def get_queryset(self):
start_date = datetime.strptime(self.request.GET.get('start_date'), '%Y-%m-%d')
end_date = datetime.strptime(self.request.GET.get('end_date'), '%Y-%m-%d')
# As Willem suggested in the comments, it is easier to check for available items
available_items = (
Item.objects.filter(booking__start_date__gte = start_date, booking__end_date__lte = end_date)
)
if start_date and end_date:
return available_items
else:
return Item.objects.all()
【问题讨论】:
我认为这里的条件是不充分的,因为start_date
可能不在form_start_date
和form_end_date
之间,end_date
也不可能在这两点之间,但是仍然预订重叠。例如因为预订跨度大于表单的跨度(开始较早,结束较晚)。
感谢您的关注,我添加了另一个规则来捕捉这一点。
【参考方案1】:
我们先分析一下两个区间(f1, t1)和(f2 sub>, t2) 重叠。要解决的一个更简单的问题是找出两个间隔何时不重叠。这适用于两种情况:
-
鉴于 t12,因此第一个事件在第二个之前结束;或
鉴于 t21,因此第一个事件在第二个事件开始之前结束。
这意味着两个事件重叠给定 t1 ≥ f2 和 t2 ≥f1.
有了这些知识,我们可以设计如下过滤器:
bookings = Booking.objects.filter(
end_date__gte=form_start_date,
start_date__lte=form_end_date
)
return Item.objects.exclude(
booking__in=bookings
)
这会导致如下查询:
SELECT item.*
FROM item
WHERE NOT (
item.id IN (
SELECT V1.item_id
FROM booking V1
WHERE (V1.id IN (
SELECT U0.id FROM booking U0
WHERE (U0.end_date >= 2019-01-01 AND U0.start_date <= 2019-02-02)
AND V1.item_id IS NOT NULL
)
)
)
(这里2019-01-01
和2019-02-02
是假设的开始和结束日期)。
我认为通过Form
处理这两个日期可能会更好,但是要进行适当的验证和清理。
例如,如果我们使用问题中提供的数据填充一个空数据库,我们会得到:
>>> i1 = Item.objects.create(name='Item #01')
>>> i2 = Item.objects.create(name='Item #02')
>>> b1 = Booking.objects.create(item=i1, start_date=)
KeyboardInterrupt
>>> from datetime import date
>>> b1 = Booking.objects.create(item=i1, start_date=date(2019,1,1), end_date=date(2019, 1, 3))
>>> b2 = Booking.objects.create(item=i1, start_date=date(2019,1,11), end_date=date(2019, 1, 18))
>>> bookings = Booking.objects.filter(
... end_date__gte=date(2019, 1, 6),
... start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
... booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>, <Item: Item object (3)>]>
>>> b3 = Booking.objects.create(item=i2, start_date=date(2019,1,2), end_date=date(2019, 1, 6))
>>> bookings = Booking.objects.filter(
... end_date__gte=date(2019, 1, 6),
... start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
... booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>]>
所以首先我构建了两个项目,并在第一个项目上进行了两次预订。如果我们然后进行查询,我们会看到两个项目都弹出。如果我们随后为Item #02
添加一个额外的预订,然后如果我们再次执行查询,我们会看到只有第一个项目(我首先为测试目的制作了一个项目,然后被删除)显示,因为对于给定的范围,第二项不再可用:已通过预订b3
预订。
【讨论】:
感谢您的回复,其他逻辑,以检查 form_end_date > form_start_date 是否将由 clean_method 处理。这是我遇到问题的可用性,所以我想简化这个例子。奇怪的是,当我为“预订”而不是“项目”创建 ListView 时,逻辑似乎有效,但在一段时间内显示包含某些预订的列表是愚蠢的。这是我们追求的可用项目 那么上面的还是不行?因为,好吧,根据它生成的 SQL 输出,查询的逻辑似乎是有效的。 我在创建预订时测试了 save 方法的逻辑,它确实有效,但是当我尝试在给定日期显示包含所有可用项目的列表时,它不会显示两者之间的可用天数同一项目的两次预订。 @KevinD:这是一个更难的问题,因为表中没有“顺序”。您可以按预订日期排序(例如先在start_date
,然后在end_date
)。但是在 Python 级别计算两个这样的行之间的天数差异可能更好。在 SQL 中,很难获得“上一个”和“下一个”行并在它们之间进行处理。以上只是检索在给定范围内没有“活动”预订的所有项目。
亲爱的 Willem,我想我的解释不是很清楚,因为我正在查询 Booking 模型,并检查用户输入和现有记录之间的任何冲突,它不应该冲突。我已经编辑了我的示例,当我选择日期 [O] 时,项目 #01 显示为不可用,它显示了不应该发生的冲突......以上是关于可用项目的 Django ListView 查询集的主要内容,如果未能解决你的问题,请参考以下文章