实例公共方法
attribute(name, cast_type = nil, **options) 链接
定义模型上带类型的属性。如果需要,它将覆盖现有属性的类型。这允许控制值在分配给模型时如何转换为 SQL 和从 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
脏跟踪¶ ↑
属性的类型有机会改变脏跟踪的执行方式。ActiveModel::Dirty 将调用 changed? 和 changed_in_place? 方法。有关这些方法的更多详细信息,请参阅 ActiveModel::Type::Value 的文档。
来源: 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 14
define_attribute( name, cast_type, default: NO_DEFAULT_PROVIDED, user_provided_default: true ) 链接
此 API 只接受类型对象,并会立即执行其工作,而不是等待加载模式。虽然此方法是为了插件作者使用而提供的,但应用程序代码可能更喜欢使用 ClassMethods#attribute。
参数¶ ↑
name-
正在定义的属性的名称。预期为
String。 cast_type-
用于此属性的类型对象。
default-
未提供值时使用的默认值。如果未传递此选项,将使用先前(如果有)的默认值。否则,默认值为
nil。也可以传递一个 proc,它将在每次需要新值时被调用一次。 - user_provided_default
-
是否应使用
cast或deserialize来转换默认值。
来源: 显示 | 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 243 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。
此方法将访问数据库并加载模型的模式(如果需要)。
来源: 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 269
实例保护方法
reload_schema_from_cache(*) 链接
来源: 显示 | 在 GitHub 上
# File activerecord/lib/active_record/attributes.rb, line 281 def reload_schema_from_cache(*) reset_default_attributes! super end