我有以下型号:
class Property(models.Model):
name = models.CharField(max_length=100)
def is_available(self, avail_date_from, avail_date_to):
# Check against the owner's specified availability
available_periods = self.propertyavailability_set \
.filter(date_from__lte=avail_date_from, \
date_to__gte=avail_date_to) \
.count()
if available_periods == 0:
return False
return True
class PropertyAvailability(models.Model):
de_property = models.ForeignKey(Property, verbose_name='Property')
date_from = models.DateField(verbose_name='From')
date_to = models.DateField(verbose_name='To')
rate_sun_to_thurs = models.IntegerField(verbose_name='Nightly rate: Sun to Thurs')
rate_fri_to_sat = models.IntegerField(verbose_name='Nightly rate: Fri to Sat')
rate_7_night_stay = models.IntegerField(blank=True, null=True, verbose_name='Weekly rate')
minimum_stay_length = models.IntegerField(default=1, verbose_name='Min. length of stay')
class Meta:
unique_together = ('date_from', 'date_to')
本质上,每个
Property
都有其可用性指定的PropertyAvailability
实例。由此,Property.is_available()
方法通过查询Property
来检查PropertyAvailability
在给定时间段内是否可用。此代码工作正常,但以下情况除外:
示例数据
使用当前的
Property.is_available()
方法,如果我要搜索2017年1月2日到2017年1月5日之间的可用性,它将起作用,因为它匹配1。但如果我在2017年1月4日和2017年1月8日之间搜索,它不会返回任何内容,因为日期范围在多个结果之间重叠-它既不匹配1也不匹配2。
I read this earlier(它通过合并结果引入了一个类似的问题和解决方案),但是在使用Django的ORM或让它与原始SQL一起工作时遇到了困难。
那么,如何编写一个查询(最好使用ORM)来实现这一点呢?或者也许有一个更好的解决方案,我不知道?
其他注意事项
avail_date_from
和avail_date_to
必须与PropertyAvailability
的date_from
和date_to
字段匹配:avail_date_from
必须>=PropertyAvailability.date_from
avail_date_to
必须PropertyAvailability.date_to这是因为我需要查询
Property
在给定的时间段内是否可用。软件规格
姜戈1.11
PostgreSQL 9.3.16版
最佳答案
我的解决方案是检查date_from
的date_to
或PropertyAvailability
字段是否包含在我们感兴趣的时段中。我是用Q
对象来做的。如上所述,我们还需要包含PropertyAvailability
对象,这些对象包含我们感兴趣的整个时段。如果找到多个实例,则必须检查可用性对象是否连续。
from datetime import timedelta
from django.db.models import Q
class Property(models.Model):
name = models.CharField(max_length=100)
def is_available(self, avail_date_from, avail_date_to):
date_range = (avail_date_from, avail_date_to)
# Check against the owner's specified availability
query_filter = (
# One of the records' date fields falls within date_range
Q(date_from__range=date_range) |
Q(date_to__range=date_range) |
# OR date_range falls between one record's date_from and date_to
Q(date_from__lte=avail_date_from, date_to__gte=avail_date_to)
)
available_periods = self.propertyavailability_set \
.filter(query_filter) \
.order_by('date_from')
# BEWARE! This might suck up a lot of memory if the number of returned rows is large!
# I do this because negative indexing of a `QuerySet` is not supported.
available_periods = list(available_periods)
if len(available_periods) == 1:
# must check if availability matches the range
return (
available_periods[0].date_from <= avail_date_from and
available_periods[0].date_to >= avail_date_to
)
elif len(available_periods) > 1:
# must check if the periods are continuous and match the range
if (
available_periods[0].date_from > avail_date_from or
available_periods[-1].date_to < avail_date_to
):
return False
period_end = available_periods[0].date_to
for available_period in available_periods[1:]:
if available_period.date_from - period_end > timedelta(days=1):
return False
else:
period_end = available_period.date_to
return True
else:
return False
不过,我觉得有必要指出,数据库模型不能保证数据库中没有重叠的
PropertyAvailability
对象。此外,唯一约束很可能包含de_property
字段。