Django模型的体系结构,用于实现时间表(调度)功能

Dmy*_*hyn 6 database django postgresql

我正在构建Django应用程序,旨在维护10K +人(以及将来更多)的时间表(时间表).基本上,问题陈述如下:每个人都有单独的时间表,下一年有免费插槽.它是离散的,步骤为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'))
Run Code Online (Sandbox Code Playgroud)

我们在即将到来的一年中每隔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')
Run Code Online (Sandbox Code Playgroud)

解决方案№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)
Run Code Online (Sandbox Code Playgroud)

获取空闲时间列表也很容易(person.slots).在特定时间(例如10-10:45)让所有人自由将是:

TimeSlot.objects.filter(time_start__gte="10:00", time_end__lte="10:45").values('person')
Run Code Online (Sandbox Code Playgroud)

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

所以我的问题如下,django开发人员如何实现此功能以便为大数据上的两个查询提供速度?对于我目前的解决方案或新想法可能的警告/好处,我将不胜感激.

iul*_*ian 5

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

第 1 部分 - 数据编码

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

第 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
Run Code Online (Sandbox Code Playgroud)

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

假设我们想找出上午 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 个插槽,我们需要在我们的 中将其设置为 1 regular_expression,从而获得'^[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点有空的人)
  • 相对简单的实现
  • 作为副作用,您将可以更轻松地找到繁忙的时间段,因为您将在数据库中拥有所需的信息并且不需要为其运行计算

缺点

  • 不冗长,对于某些人来说可能会令人困惑
  • 需要更多的数据库空间,因为您将节省空闲和繁忙的时间段