如何使用Rails 5的ActiveRecord属性提供虚拟列

时间:2019-02-21 15:02:25

标签: ruby-on-rails ruby-on-rails-5 rails-activerecord

我想将虚拟列添加到某些模型中,但希望通过诸如Product.first之类的ActiveRecord语句返回它们的值,以便可以使用Product.first.to_json之类的语句来输出产品,虚拟列,应API请求。

列的值取决于其他模型属性。我希望这些列保留到数据库中。

我尝试过:

class Product < ApplicationRecord
  def total
    price + tax
  end
end

但是Product.first不包括总数。

class Product < ApplicationRecord
  attribute :total, :decimal, default: -> { 0.0 }
end

向返回的对象添加total: 0.0,但是

class Product < ApplicationRecord
  attribute :total, :decimal, default: -> { price + tax }
end

出现诸如

之类的消息失败
#<NameError: undefined local variable or method `price' for #<Class:0x0000557b51c2c960>>

class Product < ApplicationRecord
  attribute :total, :decimal, default: -> { 0.0 }

  def total
    price + tax
  end
end

仍然返回total: 0.0

我什至不确定attribute是否是执行此操作的正确方法,因为文档似乎暗示它已绑定到列。

总结:

  • products表中不应包含total列。
  • 通过ActiveRecord访问Product应该返回一个Product对象,该对象包括一个total键,该键具有基于模型的其他属性的计算值。

这有可能吗?

真的不想用大量手动插入这些虚拟列的代码替换每个to_json调用...

3 个答案:

答案 0 :(得分:2)

您可以使用methods选项

class Product < ApplicationRecord
  def total
    price + tax
  end
end

Product.first.to_json(methods: :total)

答案 1 :(得分:1)

在模型中覆盖as_json以包含您的方法。

这不会在您检索到的Product对象中包含total,但是在对该对象调用.to_json时会包含total。

class Product < ApplicationRecord
  attribute :total, :decimal, default: -> { 0.0 }

  def total
    price + tax
  end

  def as_json(options = {})
    super(methods: [:total])
  end
end

答案 2 :(得分:0)

数据库中的virtual/generated column(假设MySQL / MariaDB)可以解决您的需求。由于它是从其他列的数据生成的,因此您无法对其进行写入,并且只能在读取操作期间对其进行更新。可以选择保留数据,但这不是这里的问题。

在我的示例中,我想向People数据库中添加一个虚拟列“ age”,该列是person.birthday和curdate()之间的区别。 我生成列:

rails generate migration AddAgeToPeople age:virtual

然后我编辑迁移文件,以使add_column :people, :age, :virtual 变为

class AddAgeToPeople < ActiveRecord::Migration[5.2]
  def change
    add_column :people, :age, :int, as: "timestampdiff(year, birthday, curdate())"
  end
end

最终结果将是如下所示的SQL:

ALTER TABLE people ADD COLUMN age GENERATED ALWAYS AS (timestampdiff(year, birthday, curdate()));

| Field | Type | Null | Key | Default | Extra | | age | int(11) | YES | | NULL | VIRTUAL GENERATED |

最终结果是模型中可以正常交互的属性(尽管只读)