加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 当验证在另一个表上有条件时,数据库中的唯一性

发布时间:2020-12-17 03:07:13 所属栏目:百科 来源:网络整理
导读:我在 Uniqueness validation in database when validation has a condition问了一个类似的问题,但是我的要求发生了变化,因此这个问题. 当有多个进程时,在Rails中使用唯一性验证是不安全的,除非在数据库上也强制执行约束(在我的例子中是PostgreSQL数据库,所以
我在 Uniqueness validation in database when validation has a condition问了一个类似的问题,但是我的要求发生了变化,因此这个问题.

当有多个进程时,在Rails中使用唯一性验证是不安全的,除非在数据库上也强制执行约束(在我的例子中是PostgreSQL数据库,所以参见http://robots.thoughtbot.com/the-perils-of-uniqueness-validations).

在我的例子中,唯一性验证是有条件的:只有在另一个模型上的另一个属性变为真时才应该强制执行.所以我有

class Parent < ActiveRecord::Base
  # attribute is_published
end

class Child < ActiveRecord::Base
  belongs_to :parent

  validates_uniqueness_of :text,if: :parent_is_published?

  def parent_is_published?
    self.parent.is_published
  end
end

所以模型Child有两个属性:parent_id(与Parent关联)和text(文本属性).模型Parent有一个属性:is_published(布尔值).如果其parent.is_published为true,则所有类型为Child的模型的文本应该是唯一的.

使用http://robots.thoughtbot.com/the-perils-of-uniqueness-validations中建议的唯一索引太过限制,因为无论is_published的值如何,它都会强制执行约束.

是否有人知道PostgreSQL数据库上依赖于另一个表的“条件”索引? Uniqueness validation in database when validation has a condition的解决方案是您的条件取决于同一个表中的属性.还是另一种解决方法?

解决方法

不幸的是,没有像 your previous question那样简单和干净的解决方案.

这应该做的工作:

>将一个冗余标志is_published添加到Child表中

ALTER TABLE child ADD column is_published boolean NOT NULL;

在插入时将其设置为DEFAULT FALSE或您在父列中通常拥有的任何内容.
它必须是NOT NULL才能避免使用NULL值的漏洞和外键中的默认MATCH SIMPLE行为:
Two-column foreign key constraint only when third column is NOT NULL
>在父级上添加(看似毫无意义的)唯一约束(parent_id,is_published)

ALTER TABLE parent ADD CONSTRAINT parent_fk_uni
UNIQUE (parent_id,is_published);

由于parent_id是主键,因此组合在任何一种方式都是唯一的.但这是以下fk约束所必需的.
>不使用简单的foreign key constraint引用父(parent_id),而是使用ON UPDATE CASCADE在(parent_id,is_published)上创建多列外键.
这样,child.is_published的状态由系统自动维护和强制执行,并且比使用自定义触发器实现的更可靠:

ALTER TABLE child
ADD CONSTRAINT child_special_fkey FOREIGN KEY (parent_id,is_published)
REFERENCES parent (parent_id,is_published) ON UPDATE CASCADE;

>然后像上一个答案一样添加partial UNIQUE index.

CREATE UNIQUE INDEX child_txt_is_published_idx ON child (text)
WHERE is_published;

当然,在子表中插入行时,您必须立即使用parent.is_published的当前状态.但重点是:强制执行参照完整性.

完整的架构

或者,不是调整现有的架构,而是完整的布局:

CREATE TABLE parent(
    parent_id serial PRIMARY KEY,is_published bool NOT NULL DEFAULT FALSE
--,more columns ...,UNIQUE (parent_id,is_published)   -- required for fk
);

CREATE TABLE child (
    child_id serial PRIMARY KEY,parent_id integer NOT NULL,is_published bool NOT NULL DEFAULT FALSE,txt text,FOREIGN KEY (parent_id,is_published)
      REFERENCES parent (parent_id,is_published) ON UPDATE CASCADE
);

CREATE UNIQUE INDEX child_txt_is_published_idx ON child (text)
WHERE is_published;

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读