python - 在 Django Admin 或 Form 中清除预期的 ExclusionConstraint IntegrityError
问题描述
文档展示了一种接受 PostgreSQL 提供的数据完整性约束的方法,例如ExclusionConstraint
重叠Ranges。您可以从此处的文档中阅读建议的解决方案。我想要一个预订系统,以确保在重叠的时间段内不能两次预订“事物”(这里是培训师/老师)。我将使用文档中的第二个示例,其中重叠标准来自现有字段:
from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import (
DateTimeRangeField,
RangeBoundary,
RangeOperators,
)
from django.db import models
from django.db.models import Func, Q
class TsTzRange(Func):
function = 'TSTZRANGE'
output_field = DateTimeRangeField()
class Reservation(models.Model):
trainer = models.ForeignKey('Trainer', on_delete=models.CASCADE)
start = models.DateTimeField()
end = models.DateTimeField()
cancelled = models.BooleanField(default=False)
class Meta:
constraints = [
ExclusionConstraint(
name='exclude_overlapping_reservations',
expressions=(
(TsTzRange('start', 'end', RangeBoundary()), RangeOperators.OVERLAPS),
('trainer', RangeOperators.EQUAL),
),
condition=Q(cancelled=False),
),
]
所以,这对我来说很好,当尝试保存无效的范围时,我会得到预期的 IntegrityError:
IntegrityError at /admin/trainer/trainingevent/add/
conflicting key value violates exclusion constraint "exclude_overlapping_reservations"
DETAIL: Key (tstzrange(start, "end", '[)'::text), trainer_id)=(["2020-12-19 16:20:00+00","2020-12-19 16:55:00+00"), 1) conflicts with existing key (tstzrange(start, "end", '[)'::text), trainer_id)=(["2020-12-19 16:15:00+00","2020-12-19 16:45:00+00"), 1).
这引出了我的问题:如何验证字段,或者更确切地说,如何clean()
在不复制功能的情况下验证输入?从我目前的角度来看,最好的办法是让 PostgreSQL 以某种方式进行检查——或者以某种方式将模型保存在 try catch 块中。
因此,在更一般的情况下,问题应该等于“如何在 Django 中清理 IntegrityError”。
可悲的是,我在文档中或其他任何地方都找不到任何关于此的内容,因此请提前感谢任何提示。
解决方案
所以,我想不可能重用约束来查询重叠事件,因为此时 Django 试图将一个对象保存到数据库中,它已经必须被清理,至少从我能找到的内容来看。无论如何,我当然会让约束保持活动状态,因为这是对我的数据完整性的有力保证,但是为了获得我正在寻找的功能,我在模型中添加了一个自定义管理器。
如下所示,它适合“带预订的房间”模式(只需将 Trainer 替换为 Room),但您需要根据需要对其进行修改。
from django.db import models
class TrainingEventManager(models.Manager):
def check_availability(self, trainer, start, end):
return not (
super()
.get_queryset()
.filter(trainer=trainer)
.filter(start__gte=start)
.filter(end__lte=end)
.exists()
)
这查询给定的日期时间范围(start__gte
和end__lte
),检查它是否有条目(exists()
)并返回相反的布尔值,因为我想知道可用性而不是“is_reserved”答案。
推荐阅读
- django - 处理 Django 模型字段中的重复属性,寻找最佳实践
- c++ - C++ 非常初学者的消息-帮助赞赏
- javascript - 如果是星期天,JavaScript 日历不显示月份的第一天
- bash - 什么是 Bash 命令或脚本,用于从包含格式日期的多行信息的文件中提取文件名:文件名:文件状态
- css - Google Web Designer 中的等效电影剪辑
- google-sheets - 谷歌表格时间值
- python-3.x - 通过python3计算煎饼对地址
- python - 来自 Python 的 DB2 连接 - ibm_db.connect 连续运行
- python - 使用 tkinter 刷新,文本输出缓慢
- r - 如何测试 R 中的线性混合效应模型(lmer)是否大于 1?