【问题标题】:Rails: validate date range uniqueness at database levelRails:在数据库级别验证日期范围的唯一性
【发布时间】:2016-03-14 12:16:51
【问题描述】:

我有一个包含这 3 个模型的 Rails 应用程序 - RentingUnit、Tenant 和 Booking。

租户可以通过填写包含以下字段的新预订表格来预订 RentingUnit -renting_unit_id、tenant_id、start_date、end_date。

start_date 和 end_date 共同构成了renting_unit 的预订期限。

这样,我想确保在与已预订的任何持续时间重叠的持续时间内无法预订renting_unit。 (如果这很重要,我正在使用 PostgreSQL 数据库。)

我遇到了与模型级别验证相关的答案,但我也想在数据库级别强制执行唯一性,以解决可能的竞争条件。

我该如何实施?

【问题讨论】:

  • 如果您要存储日期范围,那么我认为您需要一个触发器来强制执行唯一性。

标签: ruby-on-rails postgresql indexing


【解决方案1】:

在数据库中强制执行这种事情确实是一个明智的想法。幸运的是,PostgreSQL 让这一切变得简单。我不知道 Rails 中是否有对此的内置支持,如果没有,您将需要运行一些自定义 SQL。

CREATE TABLE booking (tenant_id int, unit_id int, start_date date, end_date date);
-- This is the constraint that prevents overlapping bookings
ALTER TABLE booking ADD constraint no_overlap 
  EXCLUDE USING gist (
    unit_id WITH =, 
    daterange(start_date, end_date, '[]') WITH &&
  );
-- These two abut, so that's fine
INSERT INTO booking VALUES (101, 1, '2015-01-01', '2015-01-31');
INSERT INTO booking VALUES (102, 1, '2015-02-01', '2015-02-31');
-- This one overlaps the second one, so it should fail
INSERT INTO booking VALUES (103, 1, '2015-02-01', '2015-02-01');
ERROR:  conflicting key value violates exclusion constraint "no_overlap"
DETAIL: ...

注意事项。

  1. 您需要安装 btree_gist 扩展以支持相等检查
  2. daterange(..., '[]') 表示范围的两端都包含在内。您当然可以选择专属范围。
  3. 可以说您应该直接存储范围而不是单独的日期,因为这是您希望建模的内容。
  4. 我自己更喜欢复数表名,但我不知道 rails 更喜欢什么。

详细信息和示例一如既往地在PostgreSQL Documentation 中。

【讨论】:

  • 感谢您的回复。 daterange 类型是 Postgres 特有的,所以我不倾向于第一次使用它。但我终于要去做了。我启用了 btree_gist 扩展,添加了一个名为 daterange 类型的持续时间的字段,并对重叠的预订持续时间实施了约束。
  • 对于#4,Rails 遵循复数表名和单数模型名的约定。所以我的模型名称是 RentingUnit、Tenant 和 Booking;它们对应的表名分别是renting_units、tenants & bookings。
  • 实际上,如果您想使用高级功能,恐怕您最终会为不同的 RDBMS 提供不同的实现。不过,这确实为您提供了保证的一致性,这是您的数据库的主要目的之一。
【解决方案2】:

我遇到了类似的问题,上面的答案对我不起作用。

做了以下事情:

execute <<~SQL
  ALTER TABLE booking
  ADD CONSTRAINT unique_time_range_per_unit_id
  EXCLUDE USING GIST (
    (unit_id::text) WITH =,
    tsrange(start_date, end_data) WITH &&
  );
SQL

在 Postgres 9.6 上测试

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    相关资源
    最近更新 更多