Active Record 屬性
執行個體公開方法
attribute(name, cast_type = nil, **options) 連結
在此模型中定義帶有類型屬性的屬性。如果需要,它會覆寫現有屬性的類型。這允許控制當值指派給模型時,如何將值轉換成和轉換出 SQL。它也會改變傳遞到 ActiveRecord::Base.where 的值的行為。這將讓您可以在大量的 Active Record 中使用您的網域物件,而不需要依賴於實作細節或猴子修補。
name
要為其定義屬性方法的屬性名稱,以及它儲存的欄位。
cast_type
符號,如 :string
或 :integer
,或要對此屬性使用的類型物件。如果沒有傳遞這個參數,將會使用先前定義的類型(如果有)。否則,類型將會是 ActiveModel::Type::Value
。有關提供自訂類型物件的更多資訊,請參閱以下範例。
選項
接受以下選項
default
當沒有提供值時,要使用的預設值。如果沒有傳遞這個選項,將會使用父類別或架構中先前定義的預設值(如果有)。否則,預設值將會是 nil
。
array
(僅限 PostgreSQL)指定類型應該是陣列(請見下例)。
range
(僅限 PostgreSQL)指定類型應該是範圍(請見下例)。
當對 cast_type
使用符號時,會將額外的選項轉寄給類型物件的建構函式。
範例
Active Record 偵測到的類型可以被覆寫。
# db/schema.rb
create_table :store_listings, force: true do |t|
t.decimal :price_in_cents
end
# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
end
store_listing = StoreListing.new(price_in_cents: '10.1')
# before
store_listing.price_in_cents # => BigDecimal(10.1)
class StoreListing < ActiveRecord::Base
attribute :price_in_cents, :integer
end
# after
store_listing.price_in_cents # => 10
也可以提供預設值。
# db/schema.rb
create_table :store_listings, force: true do |t|
t.string :my_string, default: "original default"
end
StoreListing.new.my_string # => "original default"
# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
attribute :my_string, :string, default: "new default"
end
StoreListing.new.my_string # => "new default"
class Product < ActiveRecord::Base
attribute :my_default_proc, :datetime, default: -> { Time.now }
end
Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
屬性不需要由資料庫欄位作為後盾。
# app/models/my_model.rb
class MyModel < ActiveRecord::Base
attribute :my_string, :string
attribute :my_int_array, :integer, array: true
attribute :my_float_range, :float, range: true
end
model = MyModel.new(
my_string: "string",
my_int_array: ["1", "2", "3"],
my_float_range: "[1,3.5]",
)
model.attributes
# =>
{
my_string: "string",
my_int_array: [1, 2, 3],
my_float_range: 1.0..3.5
}
將選項傳遞到類型建構函式
# app/models/my_model.rb
class MyModel < ActiveRecord::Base
attribute :small_int, :integer, limit: 2
end
MyModel.create(small_int: 65537)
# => Error: 65537 is out of range for the limit of two bytes
建立自訂類型
使用者也可以定義自己的自訂類型,只要它們可以回應值類型中定義的方法即可。方法 deserialize
或 cast
將會在您的類型物件上呼叫,並使用來自資料庫或控制器的原始輸入。有關預期的 API,請參閱 ActiveModel::Type::Value
。建議讓您的類型物件繼承既有的類型,或從 ActiveRecord::Type::Value
class PriceType < ActiveRecord::Type::Integer
def cast(value)
if !value.kind_of?(Numeric) && value.include?('$')
price_in_dollars = value.gsub(/\$/, '').to_f
super(price_in_dollars * 100)
else
super
end
end
end
# config/initializers/types.rb
ActiveRecord::Type.register(:price, PriceType)
# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
attribute :price_in_cents, :price
end
store_listing = StoreListing.new(price_in_cents: '$10.00')
store_listing.price_in_cents # => 1000
有關建立自訂類型的更多詳細資訊,請參閱 ActiveModel::Type::Value
文件。有關註冊您的類型以讓符號引用的更多詳細資訊,請參閱 ActiveRecord::Type.register
。您也可以直接傳遞類型物件,取代符號。
查詢
呼叫 ActiveRecord::Base.where 時,它將會使用模型類別定義的類型來將值轉換成 SQL,呼叫類型物件上的 serialize
。例如
class Money < Struct.new(:amount, :currency)
end
class PriceType < ActiveRecord::Type::Value
def initialize(currency_converter:)
@currency_converter = currency_converter
end
# value will be the result of +deserialize+ or
# +cast+. Assumed to be an instance of +Money+ in
# this case.
def serialize(value)
value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
value_in_bitcoins.amount
end
end
# config/initializers/types.rb
ActiveRecord::Type.register(:price, PriceType)
# app/models/product.rb
class Product < ActiveRecord::Base
currency_converter = ConversionRatesFromTheInternet.new
attribute :price_in_bitcoins, :price, currency_converter: currency_converter
end
Product.where(price_in_bitcoins: Money.new(5, "USD"))
# SELECT * FROM products WHERE price_in_bitcoins = 0.02230
Product.where(price_in_bitcoins: Money.new(5, "GBP"))
# SELECT * FROM products WHERE price_in_bitcoins = 0.03412
追蹤變動
屬性的類型會被賦予機會來變更追蹤變動的方式。方法 changed?
和 changed_in_place?
會從 ActiveModel::Dirty
呼叫。有關 ActiveModel::Type::Value
中這些方法的文件,請參閱更多詳細資訊。
來源: 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 13
define_attribute( name, cast_type, default: NO_DEFAULT_PROVIDED, user_provided_default: true ) 連結
此 API 僅接受型別物件,並且會立即執行它的工作,而不是等到 schema 載入。雖然這個方法是提供給外掛程式作者使用的,但應用程式程式碼可能應該使用 ClassMethods#attribute
。
name
定義的屬性的名稱。預期是 String
。
cast_type
要用於這個屬性的型別物件。
default
當沒有提供值時要使用的預設值。如果未傳遞此選項,將會使用先前的預設值(如果有的話)。否則,預設值會為 nil
。也可以傳遞一個程序,並且每次需要新值時就會呼叫它一次。
user_provided_default
預設值是否應該使用 cast
或 deserialize
進行轉換。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 231 def define_attribute( name, cast_type, default: NO_DEFAULT_PROVIDED, user_provided_default: true ) attribute_types[name] = cast_type define_default_attribute(name, default, cast_type, from_user: user_provided_default) end
type_for_attribute(attribute_name, &block) 連結
請參閱 ActiveModel::Attributes::ClassMethods#type_for_attribute
。
此方法將存取資料庫並在必要時載入模型的 schema。
來源:在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 256
受保護的實體方法
reload_schema_from_cache(*) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 268 def reload_schema_from_cache(*) reset_default_attributes! super end