防止 Django 模型中的 DateRangeField 重叠?

问题描述 投票:0回答:2

既然 Django 支持 DateRangeField,是否有一种“Pythonic”方法来防止记录具有重叠的日期范围?

假设用例

一个假设的用例是预订系统,您不希望人们同时预订相同的资源。

假设的示例代码

class Booking(models.model):
    # The resource to be reserved
    resource = models.ForeignKey('Resource')
    # When to reserve the resource
    date_range = models.DateRangeField()

    class Meta:
        unique_together = ('resource', 'date_range',)
python django postgresql django-models
2个回答
9
投票

我知道答案已经很旧了,但现在你可以在模型的元中创建一个约束,这将使 Postgres 处理这个问题

from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
from django.db import models
from django.db.models import Q

class Room(models.Model):
    number = models.IntegerField()


class Reservation(models.Model):
    room = models.ForeignKey('Room', on_delete=models.CASCADE)
    timespan = DateTimeRangeField()
    cancelled = models.BooleanField(default=False)

    class Meta:
        constraints = [
            ExclusionConstraint(
                name='exclude_overlapping_reservations',
                expressions=[
                    ('timespan', RangeOperators.OVERLAPS),
                    ('room', RangeOperators.EQUAL),
                ],
                condition=Q(cancelled=False),
            ),
        ]

毕业后的限制

为了使其工作,您还需要激活 PostgreSQL 上的

btree_gist
扩展。

您可以通过在迁移文件中添加以下行来完成此操作

from django.contrib.postgres.operations import BtreeGistExtension

class Migration(migrations.Migration):

    operations = [
         BtreeGistExtension(),
         ...
    ]

3
投票

您可以在模型

full_clean
方法中检查这一点,该方法在 ModelForm 验证期间自动调用。 如果你直接保存对象,它不会被自动调用。这是 Django 验证的一个已知问题,你可能已经意识到了! 因此,如果您希望在保存对象时随时进行验证,则还必须重写模型
save
方法。

class Booking(models.model):

    def full_clean(self, *args, **kwargs):
        super(Booking, self).full_clean(*args, **kwargs)

        o = Booking.objects.filter(date_range__overlap=self.date_range).exclude(pk=self.pk).first()
        if o:
            raise forms.ValidationError('Date Range overlaps with "%s"' % o)

    # do not need to do this if you are only saving the object via a ModelForm, since the ModelForm calls FullClean.
    def save(self):
        self.full_clean()
        super(Booking, self).save()
© www.soinside.com 2019 - 2024. All rights reserved.