Django存储实际可用性并查询新预订的可用时间表

2024-05-16 08:40:28 发布

您现在位置:Python中文网/ 问答频道 /正文

在应用程序中,有两种类型的用户: StudentMentor

  1. 作为mentor用户类型,我可以设置每周的可用性
  2. 作为student用户类型,我可以看到mentor可用性

============================================================

因此,我在WeekDayAvailableHour模型中存储基于周日的用户可用性

DAYS_OF_WEEK = (
    (0, 'Monday'),
    (1, 'Tuesday'),
    (2, 'Wednesday'),
    (3, 'Thursday'),
    (4, 'Friday'),
    (5, 'Saturday'),
    (6, 'Sunday'),
)
        
class WeekDay(models.Model):
    mentor = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="weekday"
    )
    weekday = models.IntegerField(choices=DAYS_OF_WEEK)
    

class AvailableHour(models.Model): #Based on weekday
    weekday = models.ForeignKey(
        WeekDay,
        on_delete=models.CASCADE,
        related_name="available_hour"
    )
    from_hour = models.TimeField()
    to_hour = models.TimeField()
  1. 作为用户类型student,我只能在mentor可用小时内预订会话

因此,当学生预订课程时,我们将信息存储在模型下面

 class BookingSession(models.Model):
   student = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="student_booking"
    )
    mentor = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="mentor_booking"
    )
    date = models.DateField()
    
    from_hour = models.TimeField()
    to_hour = models.TimeField()

那么现在有什么问题吗?

因为我们有用户AvailableHour,所以一旦学生预订了导师课程,那么导师档案中指定的时间范围是不可用的,对吗?这样其他学生就可以看到导师的实际可用性

所以你知道,如果我们从AvailableHour获得queryset,这并不意味着如果任何指定的时间范围已经预订,并且预订的时间已经存储在BookingSession模型中,那么tutor在该时间内可用

如果在指定的工作日BookingSession中存在开始时间和结束时间,我想排除AvailableHour的记录

我想向学生展示,导师的实际可用性是什么

比如AvailableHour不能被存储,如果在BookingSession已经有预订,那么实际可用。你知道的

假设我在BookingSession表中有数据

booked_sesson = [
    {'date': '2021-08-02','from_hour': '12:30', 'to_hour': '12:15' }, # monday
    {'date': '2021-08-04','from_hour': '02:30', 'to_hour': '03:15' }, # Wednesday
]

以及下面的{}和{}可用性表数据

available_based_on_weekday = [
    {
        "weekday": 1, 'opening_hour_time': [ # monday
            {'from_hour': '12:30', 'to_hour': '12:15'},
            {'from_hour': '12:30', 'to_hour': '12:15'},
            {'from_hour': '12:30', 'to_hour': '12:15'}
        ]
    },
    {
        "weekday": 7, 'opening_hour_time': [ # Saturday
            {'from_hour': '13:30', 'to_hour': '15:15'},
            {'from_hour': '15:30', 'to_hour': '16:15'},
            {'from_hour': '19:30', 'to_hour': '20:15'},
            {'from_hour': '23:30', 'to_hour': '23:45'}
        ]
    },
    {
        "weekday": 3, 'opening_hour_time': [ # Wednesday
            {'from_hour': '02:30', 'to_hour': '03:30'},
            {'from_hour': '17:30', 'to_hour': '18:15'},
            {'from_hour': '19:30', 'to_hour': '20:15'},
            {'from_hour': '21:30', 'to_hour': '22:30'}
        ]
    }
]

我期望的输出结果是什么? 在查询之后,我期望得到这些输出,并应逐月进行查询:

actual_availability = [
     {
         'date': '2021-08-01',
         'available': []
     },
     {
         'date': '2021-08-02',
         'available': [
            {'from_hour': '12:30', 'to_hour': '12:15'},
            {'from_hour': '12:30', 'to_hour': '12:15'}
         ]
     },
     {
         'date': '2021-08-03',
         'available': []
     },
     {
         'date': '2021-08-04',
         'available': [
            {'from_hour': '03:15', 'to_hour': '03:30'},
            {'from_hour': '17:30', 'to_hour': '18:15'},
            {'from_hour': '19:30', 'to_hour': '20:15'},
            {'from_hour': '21:30', 'to_hour': '22:30'}
         ]
     },
     {
         'date': '2021-08-05',
         'available': []
     },
     {
         'date': '2021-08-06',
         'available': []
     },
     {
         'date': '2021-08-07',
         'available': []
     },
     {
         'date': '2021-08-08',
         'available': []
     },
     {
         'date': '2021-08-09',
         'available': [
            {'from_hour': '12:30', 'to_hour': '12:15'},
            {'from_hour': '12:30', 'to_hour': '12:15'},
            {'from_hour': '12:30', 'to_hour': '12:15'}
         ]
     },
     # to be continued for a month by month
]

如果你有任何其他的方法,我会欢迎,请在这里分享你的观点

给你我试过的,真的很愚蠢,不起作用

real_available_date_slot = []
for obj in BookingSession.objects.filter(mentor_id='A mentor ID'):
    weekday =  obj.date.isoweekday()
    real_available = []

    available_hour = AvailableHour.objects.filter(weekday__weekday=weekday, weekday__mentor=obj.mentor)
    availables = available_hour.exclude(
        from_hour__gt=obj.to_hour
    ).exclude(
        to_hour__lt=obj.to_hour
    )
    
    for available in availables:
        real_available.append(
            {'from_hour': available.from_hour, 'to_hour': available.to_hour}
        )

    real_available_date_slot.append({
        obj.date: real_available
    })

我也尝试过其他不同的方法,但我没有任何解决办法

有人能分享你的想法吗?我们如何解决我的问题?你对这类事情的质疑方式是什么?还是我为这个案子做了不恰当的数据库设计


Tags: to用户fromobjdateonmodels时间
3条回答

这将是我的方法(未经测试):

from django.db import models


class MentorUnavailable(Exception):
    pass


class Mentor(models.Model):
    def book(self, from_hour, to_hour):
        availability = self.availability.filter(from_hour__lte=from_hour, to_hour__gte=to_hour, status__in=[Availability.AVAILABLE])
        if availability.exists():
            availability = availability.first()
            return availability.split(from_hour, to_hour)  # returns an Availability with status booked
        else:
            raise MentorUnavailable


class Student(models.Model):
    bookings = models.ManyToManyField("mentor_bookings.Availability")

    def book_mentor(self, mentor: Mentor, from_hour, to_hour):
        booking = mentor.book(from_hour, to_hour)
        self.bookings.add(booking)


class Availability(models.Model):
    # status
    BOOKED = 0
    AVAILABLE = 1
    STATUS_CHOICES = [
        (BOOKED, "Booked"),
        (AVAILABLE, "Available")
    ]
    # fields
    mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE, related_name="availability")
    from_hour = models.DateTimeField()
    to_hour = models.DateTimeField()
    status = models.IntegerField(choices=STATUS_CHOICES, default=AVAILABLE)

    def split(self, from_hour, to_hour):
        if not (self.from_hour <= from_hour and self.to_hour >= to_hour):
            raise Exception
        if from_hour - self.from_hour:
            new_instance = Availability.objects.get(pk=self.pk)
            new_instance.pk = None
            new_instance.to_hour = from_hour
            new_instance.save()
        if self.to_hour - to_hour:
            new_instance = Availability.objects.get(pk=self.pk)
            new_instance.pk = None
            new_instance.from_hour = to_hour
            new_instance.save()
        self.status = Availability.BOOKED
        self.save()
        return self

这样做的目的是让导师在他有空时决定一个时间范围,当学生在其中选择一个时间范围时,可用性被分割,因此剩余的时间范围仍然可用,并且预定的时间段具有预定状态。然后,您可以将该可用性添加到学生的预订中

其优点是查询更简单,模型更少from_timeto_time将始终是datetime对象,因为您需要比较它们并进行计算。注意时区。我还建议添加一些验证器,就像其他答案中建议的那样,不允许在不合适的时间结束预订。这还将减少发生拆分时创建的可用性对象的数量。所以15分钟就可以了。它还允许您在学生取消预订时轻松合并可用性。Availability.status字段也可以是一个布尔值,但我选择它作为一个整数字段,以防您想用更多选项展开它

现在,连续化导师的可用性变得更容易了

例如,您可以将可用性保存为15分钟的时段。 在student和mentor之间没有BookingSession链接,您可以在student(用户)和AvailableHour对象之间有它的链接

 class BookingSession(models.Model):
   student = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="student_booking"
    )

AvailableHour模型中,可以有一个ForeignKey字段链接到与其相关的Bookingsession对象

# this is a timeslot now
class AvailableHour(models.Model): #Based on weekday
    weekday = models.ForeignKey(
        WeekDay,
        on_delete=models.CASCADE,
        related_name="available_hour"
    )
    from_hour = models.TimeField()
    to_hour = models.TimeField()
    booked_by = ForeignKey(User, null=True)

当过滤显示时,您只需过滤掉不包含booked_by==null的任何时隙

AvailableHour.objects.filter(weekday__weekday=weekday, weekday__mentor=obj.mentor, booked_by=null)

另一种方法,以@Jimmar的答案为基础,添加一些变体:

Availability可以保存导师信息和导师的可用日程安排。因此,导师可以在其可用性关系中有许多项,这些项将包含工作日信息以及时间段

class Availability(models.Model):
    mentor = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="availability"
    )
    weekday = models.IntegerField(choices=DAYS_OF_WEEK)
    from_hour = models.TimeField()
    to_hour = models.TimeField()

例如,导师可以提供以下服务:

{'id': 1, 'mentor': 1, 'weekday': 1, 'from_hour': '12:15', 'to_hour': '12:45'}
{'id': 2, 'mentor': 1, 'weekday': 1, 'from_hour': '12:45', 'to_hour': '13:15'}
{'id': 3, 'mentor': 1, 'weekday': 1, 'from_hour': '13:15', 'to_hour': '14:45'}

反过来,Bookings现在可以持有studentmentor_sessionAvailability)关系,但保留预定日期。这将支持获取Availability项并按预定日期排除

class BookingSession(models.Model):
    student = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="bookings"
    )
    mentor_session = models.ForeignKey(
        Availability,
        on_delete=models.CASCADE,
        related_name="bookings"
    )
    date = models.DateField()

例如,如果我们有以下预订:

{'id': 1, 'student': 1, 'mentor_session': 1, 'date': '2021-01-01'}
{'id': 2, 'student': 1, 'mentor_session': 1, 'date': '2021-01-02'}
{'id': 3, 'student': 1, 'mentor_session': 1, 'date': '2021-01-03'}

这意味着id为1的学生将在上述日期预订导师的id为1的可用性。现在,根据日期获得导师的可用时间:

date = datetime.date(2021, 01, 01)
weekday = date.isoweekday()

Availability.objects.filter(mentor=mentor, weekday=weekday).annotate(
    is_booked_on_date=Exists(
        BookingSession.objects.filter(mentor_session=OuterRef('pk'), date=date)
    )
).filter(is_booked_on_date=False)

由于id为1的可用性已在指定日期预订(它有预订会话),因此应仅返回日期2021-01-01的可用性id 2和3

为了支持您想要的预期输出,您可以在特定的日期范围内迭代每天,并使用上述查询的结果

相关问题 更多 >