如何使用Rails5的ActiveRecord属性提供虚拟列
How to use Rails 5's ActiveRecord attributes to provide a virtual column
我想将虚拟列添加到我的一些模型中,但要让它们的值由像 Product.first
这样的 ActiveRecord 语句 return 编辑,这样我就可以使用像 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
添加到 returned 对象,但是
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
仍然returns total: 0.0
.
我什至不确定 attribute
是否是正确的方法,因为文档似乎暗示它绑定到一个列。
总结一下:
products
table 不应包含 total
列。
- 通过 ActiveRecord 访问
Product
应该 return 一个包含 total
键的 Product
对象,该键具有基于模型其他属性的计算值。
这可能吗?
我 真的 不想用大量手动插入这些虚拟列的代码替换每个 to_json
调用…
您可以使用 methods
选项
class Product < ApplicationRecord
def total
price + tax
end
end
Product.first.to_json(methods: :total)
覆盖模型中的 as_json
以包含您的方法。
这不会在您检索到的 Product 对象中包含总计,但会在对该对象调用 .to_json
时包含总计。
class Product < ApplicationRecord
attribute :total, :decimal, default: -> { 0.0 }
def total
price + tax
end
def as_json(options = {})
super(methods: [:total])
end
end
A virtual/generated column (assuming 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 |
最终结果是模型中的一个属性,我可以正常交互(虽然只读)
我想将虚拟列添加到我的一些模型中,但要让它们的值由像 Product.first
这样的 ActiveRecord 语句 return 编辑,这样我就可以使用像 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
添加到 returned 对象,但是
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
仍然returns total: 0.0
.
我什至不确定 attribute
是否是正确的方法,因为文档似乎暗示它绑定到一个列。
总结一下:
products
table 不应包含total
列。- 通过 ActiveRecord 访问
Product
应该 return 一个包含total
键的Product
对象,该键具有基于模型其他属性的计算值。
这可能吗?
我 真的 不想用大量手动插入这些虚拟列的代码替换每个 to_json
调用…
您可以使用 methods
选项
class Product < ApplicationRecord
def total
price + tax
end
end
Product.first.to_json(methods: :total)
覆盖模型中的 as_json
以包含您的方法。
这不会在您检索到的 Product 对象中包含总计,但会在对该对象调用 .to_json
时包含总计。
class Product < ApplicationRecord
attribute :total, :decimal, default: -> { 0.0 }
def total
price + tax
end
def as_json(options = {})
super(methods: [:total])
end
end
A virtual/generated column (assuming 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 |
最终结果是模型中的一个属性,我可以正常交互(虽然只读)