django - Django 模型实现时间表(调度)功能的架构

标签 django database postgresql

我正在构建 Django 应用程序,旨在维护 10K+ 人(以及 future 更多人)的时间表(时间表)。基本上,问题陈述如下:每个人都有单独的时间表和明年的免费时段。它是离散的,步长为 15 分钟。我需要开发模型架构(这将暗示底层的数据库设计)来执行以下操作:

  1. 查询给定人员的所有空闲时间段。
  2. 查询某段时间内所有空闲的人。

例如,我有约翰,他在 11 月 14 日上午 8 点到下午 14 点有空,萨拉在 11 月 14 日上午 10 点到上午 11 点有空。如果我查询 John 的空闲时间段,我想得到“11 月 14 日上午 8 点至下午 14 点”。如果我查询“早上 8 点到 11 点有空的人”,我会得到约翰,因为萨拉要到上午 10 点才有空。如果我查询“上午 10 点到 11 点有空的人”,我想同时获得 John 和 Sara。 我考虑过这个问题,我的想法如下。

解决方案 №1:我们创建一个 FreeTimeSlot 模型,它将存储有关每 15 分钟时间跨度间隔的信息,并建立与人的整个关系。

    class Person(models.Model):
        name = models.CharField(max_length=32, null=False, blank=False)
        free_slots = models.ManyToManyField(FreeTimeSlot, related_name='tutor_set', null=True, blank=True, through='PersonSlot')

    class TimeSlot(models.Model):
        time = models.DateTimeField(db_index=True) #perhaps other field type

    class PersonSlot(models.Model):
        person = models.ForeignKey(Person)
        timeslot = models.ForeignKey(Slot)

        class Meta:
            db_table = 'person_free_slots'
            unique_together = (('timeslot', 'person'))

我们为来年的每 15 分钟间隔创建 365*24*4 TimeSlot 模型,如果有人在他的日程安排中表示空闲时间,我们将添加与该 TimeSlot 的关系。 使用这种架构,为个人获取空闲时间段就像通过管理器一样简单:person.free_time_slots 让所有人在特定时间(例如 10-10:45)有空也很容易,像这样平滑:

timeslots = TimeSlot.objects.filter(time__in=['10:00', '10:15', '10:30'])
PersonSlot.objects.filter(timeslot__in=timeslots).values('person')

解决方案 №2: 我们避免为每个时间段创建模型,而是在 PersonTime 模型本身中保留日期:

    class Person(models.Model):
        name = models.CharField(max_length=32, null=False, blank=False)

    class TimeSlot(models.Model):
        person = models.ForeignKey(Person, related_name='slots')
        time_start = models.DateTimeField(db_index=True)
        time_end = models.DateTimeField(db_index=True)

获取空闲时间段列表也很容易(person.slots)。让所有人在特定时间(例如 10-10:45)有空就像:

TimeSlot.objects.filter(time_start__gte="10:00", time_end__lte="10:45").values('person')

此解决方案不适用于相交间隔,我不确定查询间隔的索引时间(在同一字段上使用 gte 和 lte 比较)是否有效,并且工作速度很快。如果可以的话,我会使用 Postgres。我还用伪代码编写了时间查询以简化代码。

所以我的问题如下,django 开发人员将如何实现此功能以提供大数据查询的速度?我将不胜感激关于我当前解决方案或新想法的可能警告/好处的建议。

最佳答案

让我们把这个问题分成两部分。

第 1 部分 - 数据编码

考虑对与时隙相关的数据进行编码。如果您需要 15 分钟的精度,则您有 96 个槽(一天 1 小时 * 24 小时内有 4 个槽),任何一天的持续时间为 15 分钟。每个槽可以有两种可能的状态之一:1 - 槽空闲,0 - 槽忙(或者反之亦然,如果你愿意的话)。因此,您可以用 0 的字符串表示每日时间表。 s 和 1秒。例如,字符串(为了便于阅读而添加的空格)0000 0000 0000 0000 0000 0000 0000 0000 0000 1110 0000 ...表示 00:00AM 到 09:00AM 之间的繁忙时间段(晚上没有人工作),然后是 9:00AM 到 9:45AM 之间的空闲时间段(连续三个 1 s),然后是繁忙时间时段从上午 9:45 开始。

所以,你可以这样写你的模型:

class Person(models.Model):
    name = models.CharField(max_length=32)

class DailySchedule(models.Model):
    person = models.ForeignKey(Person, related_name='day_schedule')
    date = models.DateField()
    schedule = models.CharField(max_length=96)

第 2 部分 - 查询

所以,我们编码了有关可用/繁忙时隙的信息,但我们如何从数据库中提取它呢?幸运的是,Django 有 regex现场查找的可能性!幸运的是,Django 1.4 支持它!!

因此,为了查找在特定时间段内有空的人员,您可以使用 DailySchedule.objects.filter(date=date, schedule__regex=r'<expression>') .由于使用什么表达式来提取不同的时间范围并不明显,因此我们需要一个支持函数:

def time_slot_to_regex(start_time, end_time):
    # times should be in HH:MM format
    start_hour, start_minutes = start_time.split(':')
    end_hour, end_minutes = end_time.split(':')

    slots_before_needed_time = (int(start_hour)*4 + int(start_minutes)/15)

    # compute how many hours are between given times and find out nr of slots
    hour_duration_slots = (int(end_hour) - int(start_hour)) * 4  # 4 slots in each hour

    # adjust nr of slots according to minutes in provided times. 
    # e.g. 9:30 to 10:45 - we have 10-9=1 hour, which is 4 time slots, 
    # but we need to subtract 2 time slots, because we don't have 9:00 to 10:00, 
    # but 9:30 to 10:00 so we subtract 30/15=2 timeslots and add what is left 
    # from the incomplete hour of 10:45 time, which is 45/15 minutes = 3 slots
    minute_duration_slots = int(end_minutes)/15 - int(start_minutes)/15

    total_duration = hour_duration_slots + minute_duration_slots

    regular_expression = r'^[01]{%d}1{%d}' % (slots_before_needed_time, total_duration)

    return regular_expression

让我们弄清楚这个函数是如何工作的

假设我们想找出上午 9:15 到 9:45 之间有空的人员。我们调用slots_expression = time_slot_to_regex('9:15', '9:45')计算:

  • slots_before_needed_time = 37 ,我们将 9 乘以 4 + 15/15 得到。这是我们不关心的槽数,它将进入我们的 regular_expression 的第一部分。字符串 - '^[01]{37}'
  • hour_duration_slots = 0 , 因为两个时间值中的小时是相同的
  • minute_duration_slots = 2 ,我们通过从 45/15 中减去 15/15 得到
  • 前 2 个相加得到 2 个槽,我们需要在 regular_expression 中将其设置为 1 ,从而获得'^[01]{37}1{2}'

现在我们可以将这个正则表达式提供给我们的过滤器,获得DailySchedule.objects.filter(schedule__regex=slots_expression)瞧!我们得到了结果。

保存数据过程

我已经描述了编码数据的原理,但是没有提到编码它的过程。这可以通过使用另一个支持函数轻松完成,该函数接受一串现有的繁忙/可用插槽和一个 start_date。和 end_date为此更新现有计划。如果您也需要描述此方法,请告诉我。

优点

  • 没有 M2M 关系,这导致查询速度更快
  • 可以使用不同的正则表达式在一天内搜索几个空闲时间段(例如 ^[01]{36}1{4}[01]{24}1{4} 将查找上午 9 点到上午 10 点下午 4 点到下午 5 点有空的人<
  • 实现相对简单
  • 作为副作用,您将可以更轻松地找到繁忙的时间段,因为您将在数据库中获得所需的信息,而无需为其运行计算

缺点

  • 不冗长,对某些人来说可能会造成混淆
  • 需要更多的数据库空间,因为您要保存空闲和繁忙的时间段

关于django - Django 模型实现时间表(调度)功能的架构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33701418/

相关文章:

jquery - Django 评级和 Jquery 集成?

python - 在 ubuntu 17 上通过 wsgi 运行 django 应用程序时出现问题

python - 新手 : need some explanation on this Django url

python - 使用 Genie 在 SQLite 数据库中创建表?

postgresql - JDBCappender log4j2 和 postgres 错误

java - 更新 SQL 查询在 Java Swing 中不起作用

python - Django -objects.all() 不显示任何内容

sql - 如何查询(几乎)树结构

mysql - 在 Access 数据库(mdb 文件)中使用 Jet 表时,触发器的替代方案是什么?

sql - 如何在postgresql中拆分表