首页 > 解决方案 > 如何在 Rails ActiveRecord 中返回生成的列?

问题描述

我们想在 Rails 6 应用程序中引入一个触发器来设置列的值。该表如下所示(PostgreSQL 9.6):

CREATE TABLE foo(
    id bigserial primary key
  , sku text not null unique
  -- ...
);

在创建时,我们的代码在 SKU 上设置了一个前缀:

class Foo < ApplicationRecord
  before_create :set_sku_prefix

  private

  def set_sku_prefix
    self[:sku] = "AAA-"
  end
end

当触发器触发时,它会完成记录设置:

CREATE OR REPLACE FUNCTION generate_sku() RETURNS trigger AS $$
BEGIN
  -- Simplified, real trigger handles ID already being set,
  -- and other edge cases related to the length of the returned value.
  NEW.id = nextval('public.foos_id_seq');
  NEW.sku = NEW.sku || RIGHT('000000', NEW.id, 6);

  RETURN NEW;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER generate_sku
BEFORE INSERT OR UPDATE
ON foos
FOR EACH ROW
WHEN (RIGHT(NEW.sku, 1) = '-')
EXECUTE PROCEDURE generate_sku();

当 ActiveRecord 生成 SQL 语句来执行插入时,它会这样做:

INSERT INTO foos(sku) VALUES ('AAA-') RETURNING id

我想更改最后一条语句,使其也返回 SKU,并让 ActiveRecord 将内存中的属性值更新为数据库生成的值:

INSERT INTO foos(sku) VALUES ('AAA-') RETURNING id, sku

最后,我希望以下内容属实:

foo = Foo.create!
if foo.sku == ("AAA-%06d" % [foo.id])
  puts "All good!"
else
  puts "Oops, wrong"
end

我已经阅读了大部分ActiveRecord API 文档Ruby on Rails 指南,但无济于事。在一般网络上搜索也失败了。我搜索了“rails activerecord 触发器返回”,没有任何与我需要的东西真正相关的内容。

是否可以让 ActiveRecord 读取更多写入的行并重新加载特定属性?

我们这样做的原因是为了避免在 INSERT 上重复写入。现有代码保存,然后使用记录的 ID 在 Ruby-land 中设置 SKU,然后将列写回数据库。这意味着同一操作需要 2 次 DB 往返。SKU 列也允许为 NULL,我不太喜欢。我们想保存到数据库的往返,并NOT NULL在 SKU 列上强制执行。如果我们必须重新加载记录,这将破坏使用数据库触发器的目的。

标签: ruby-on-railspostgresqlactiverecord

解决方案


推荐阅读