获取ActiveRecord对象属性的类型

79

我想知道是否可以通过编程方式获取类型(如AR所知-例如在迁移脚本和数据库中),我知道数据在那里某处存在。

例如,我可以处理所有属性名称:

ar.attribute_names.each { |name| puts name }

.attributes 只返回当前名称到值的映射(例如,如果未设置字段,则没有类型信息)。

我看过一些地方它会带有类型信息:

在script/console中,键入AR实体的名称:

>> Driver
=> Driver(id: integer, name: string, created_at: datetime, updated_at: datetime)

很明显它知道类型。此外,还有.column_for_attribute,它接受一个属性名称并返回一个列对象 - 其中类型嵌入在底层数据库列对象中,但似乎没有干净的方法来获取它。

我也很想知道是否有一种适用于即将到来的新“ActiveModel”(rails3)且与数据库细节无关的友好方式(但也许类型信息不会是其中的一部分,我似乎找不到是否是这样)。

谢谢。

8个回答

116

在 Rails 3 中,对于你的模型 "Driver",你想要使用 Driver.columns_hash

Driver.columns_hash["name"].type  #returns :string

如果您想遍历它们,可以这样做:

Driver.columns_hash.each {|k,v| puts "#{k} => #{v.type}"}

它将输出以下内容:

id => integer
name => string
created_at => datetime
updated_at => datetime

你知道怎样测试一个值是否与一列相匹配吗?就像这样 2.is_a? Driver.columns_hash["name"].type - mariowise
在Rails 5+中,这无法与虚拟属性一起使用。 - estani

40

Rails 5中,您可以独立于数据库完成此操作。如果使用新的属性API定义(附加)属性,则这一点非常重要。

从模型类获取所有属性:

pry> User.attribute_names
=> ["id",
 "firstname",
 "lastname",
 "created_at",
 "updated_at",
 "email",...

获取类型:

pry> User.type_for_attribute('email')
=> #<ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString:0x007ffbab107698
 @limit=255,
 @precision=nil,
 @scale=nil>

有时候提供的信息比需要的多。有一个方便的函数可以将所有这些类型映射到核心集(:integer, :string等)。

> User.type_for_attribute('email').type
=> :string 

您也可以使用attribute_types一次获取所有数据,该函数返回一个'name': type哈希表。


3
这也适用于Rails 4.2。需要注意的是,type_for_attribute('id')只接受字符串作为参数。有点令人困惑的是,type_for_attribute('id').type返回的是一个符号。 - xander-miller
1
如果你所说的数据库无关性是指ORM的话,那么Mongoid还没有实现type_for_attribute。也许在Rails 5有官方发布之后会有吧。 - Cyril Duchon-Doris
在Rails 7中使用ActiveRecord Encryption时,请改用type_for_attribute(:attribute_name).cast_type.type。需要使用cast_type来读取通过类似于attribute :attribute_name, :boolean的调用设置的自定义类型。 - anothermh

23

你可以通过以下方式访问列的类型:

#script/console
Driver.columns.each {|c| puts c.type}

如果你想获取特定模型中所有列的类型列表,可以进行以下操作:

Driver.columns.map(&:type) #gets them all
Driver.columns.map(&:type).uniq #gets the unique ones

谢谢。我猜这不会成为ActiveModel的一部分,因为它绑定到列上(这可能不会出现在AM中)。但是对于AR来说,这非常棒。你知道吗,我其实在脑海深处就知道这一点,但由于某种原因被阻止了。 - Michael Neale

9
在Rails 5中,这将为您提供所有字段名称及其数据类型的列表:
Model_Name.attribute_names.each do |k| puts "#{k} = #{Model_Name.type_for_attribute(k).type}" end

注意 type_for_attribute 函数需要字符串作为参数。它可以接受符号或不存在的属性名称,但会返回一个不执行强制类型转换的 ActiveModel::Type::Value 对象。在我看来这是一个错误,我会尝试报告/修复它。 - Beni Cherniavsky-Paskin

7
Rails 5+(也适用于虚拟属性):
Model.attribute_types['some_attribute'].type

5

这段代码将会以哈希表的形式返回一个模型的所有属性和与之关联的数据库数据类型。只需将Post替换为您的Active Record模型即可。

Post.attribute_names.map {|n| [n.to_sym,Post.type_for_attribute(n).type]}.to_h

将返回如下的哈希值。
=> {:id=>:integer, :title=>:string, :body=>:text, :created_at=>:datetime, :updated_at=>:datetime, :topic_id=>:integer, :user_id=>:integer} 

2
假设Foobar是你的Active Record模型。你也可以这样做:
attributes = Foobar.attribute_names.each_with_object({}) do |attribute_name, hash|
  hash[attribute_name.to_sym] = Foobar.type_for_attribute(attribute_name).type
end

也适用于Rails 4


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接