Rails对象转换为哈希表

177
我有一个已经创建的对象,具体如下。
@post = Post.create(:name => 'test', :post_number => 20, :active => true)

保存后,我希望能够将该对象返回到哈希表中,例如通过执行以下操作:

@object.to_hash

在Rails中如何实现这个?

11个回答

343

如果你只想获取属性,那么可以通过以下方式获得:

@post.attributes

请注意,每次调用此方法都会调用 ActiveModel::AttributeSet.to_hash ,因此如果您需要多次访问哈希表,则应将其缓存到本地变量中:

attribs = @post.attributes

34
循环时不要使用这个昂贵的方法,使用as_json方法。 - AnkitG
8
若模型未完成,.to_json 将会查询数据库。 - ykhrustalev
1
使用 joinsselectPerson.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributes 可以返回以下结果: { street: "", name: "" } - Fangxing
4
我不认为as_jsonattributes更省钱。如果你查看as_json的源代码,它会调用serializable_hash,而serializable_hash又会调用attributes! 所以你的建议实际上增加了两个复杂度层次,使其更加昂贵。 - sandre89
3
.as_json将对象序列化为Ruby哈希。 - roadev
1
.to_json 将所有内容转换为字符串,而 .attributes 保留类型信息。 - Steve Folly

53

在Rails的最新版本中(虽然无法确定具体是哪个版本),您可以使用as_json方法:

@post = Post.first
hash = @post.as_json
puts hash.pretty_inspect

将输出:

{ 
  :name => "test",
  :post_number => 20,
  :active => true
}

为了更进一步,您可以重写该方法以自定义属性的显示方式,例如执行以下操作:
class Post < ActiveRecord::Base
  def as_json(*args)
    {
      :name => "My name is '#{self.name}'",
      :post_number => "Post ##{self.post_number}",
    }
  end
end

然后,使用与上述相同的实例,将输出:
{ 
  :name => "My name is 'test'",
  :post_number => "Post #20"
}

当然,这意味着您必须明确指定必须出现哪些属性。
希望这可以帮到您。
编辑:
此外,您可以检查Hashifiable gem。

谢谢,救了我的命。 - Herick

26
@object.as_json

as_json提供了一种非常灵活的配置方式,可以根据模型关系配置复杂对象。

示例

模型campaign属于shop,并且有一个list

模型list有许多list_tasks,每个list_tasks都有许多comments

我们可以轻松地获取将所有这些数据组合在一起的json。

@campaign.as_json(
    {
        except: [:created_at, :updated_at],
        include: {
            shop: {
                except: [:created_at, :updated_at, :customer_id],
                include: {customer: {except: [:created_at, :updated_at]}}},
            list: {
                except: [:created_at, :updated_at, :observation_id],
                include: {
                    list_tasks: {
                        except: [:created_at, :updated_at],
                        include: {comments: {except: [:created_at, :updated_at]}}
                    }
                }
            },
        },
        methods: :tags
    })

注意方法::tags可以帮助您附加任何与其他对象没有关系的附加对象。您只需要在模型campaign中定义一个名为tags的方法即可。该方法应返回任何您需要的内容(例如Tags.all)。

as_json的官方文档。


之前已经编写了一个自定义函数,但是想要更多的单次使用方法,而不是为类定义一个函数。即使使用 XML 序列化方法工作了一段时间,也错过了这个方法。to_ 变体似乎与 as_ 变体几乎完全相同,除了引用输出。唯一不喜欢的是没有保留我的筛选条件顺序。它似乎是按字母顺序排序的。我以为这与我环境中的 awesome_print gem 有关,但我认为情况并非如此。 - Pysis

11

你可以使用以下方法将模型对象的属性作为哈希返回:

@post.attributes
或者。
@post.as_json

as_json 允许您包括关联对象和它们的属性,并指定要包含/排除哪些属性(请参见文档)。然而,如果您只需要基本对象的属性,在我的应用程序中使用ruby 2.2.3和rails 4.2.2进行基准测试表明attributes所需时间少于as_json的一半。

>> p = Problem.last
 Problem Load (0.5ms)  SELECT  "problems".* FROM "problems"  ORDER BY "problems"."id" DESC LIMIT 1
=> #<Problem id: 137, enabled: true, created_at: "2016-02-19 11:20:28", updated_at: "2016-02-26 07:47:34"> 
>>
>> p.attributes
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> p.as_json
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> n = 1000000
>> Benchmark.bmbm do |x|
?>   x.report("attributes") { n.times { p.attributes } }
?>   x.report("as_json")    { n.times { p.as_json } }
>> end
Rehearsal ----------------------------------------------
attributes   6.910000   0.020000   6.930000 (  7.078699)
as_json     14.810000   0.160000  14.970000 ( 15.253316)
------------------------------------ total: 21.900000sec

             user     system      total        real
attributes   6.820000   0.010000   6.830000 (  7.004783)
as_json     14.990000   0.050000  15.040000 ( 15.352894)

如果您正在使用join方法运行嵌套资源,则as_json将再次调用数据库查询。 - Tony Hsieh

6
你可以使用属性来返回所有属性,但你也可以在Post中添加一个实例方法,将其命名为“to_hash”,并使其返回哈希表中所需的数据。类似于:
def to_hash
 { name: self.name, active: true }
end

6

这里有一些很好的建议。

我认为值得注意的是,您可以将ActiveRecord模型视为哈希表进行处理:

@customer = Customer.new( name: "John Jacob" )
@customer.name    # => "John Jacob"
@customer[:name]  # => "John Jacob"
@customer['name'] # => "John Jacob"

因此,不必生成属性的哈希值,而是可以将对象本身用作哈希。

2

不确定这是否是你需要的,但在 Ruby 控制台中尝试以下内容:

h = Hash.new
h["name"] = "test"
h["post_number"] = 20
h["active"] = true
h

显然,它会在控制台返回一个哈希值。如果你想从方法中返回哈希值,而不仅仅是"h",可以尝试使用"return h.inspect",类似于下面的示例:

def wordcount(str)
  h = Hash.new()
  str.split.each do |key|
    if h[key] == nil
      h[key] = 1
    else
      h[key] = h[key] + 1
    end
  end
  return h.inspect
end

海报在询问Rails中的ActiveRecord模型。 - Jeffrey Harrington

2

我的解决方案:

Hash[ post.attributes.map{ |a| [a, post[a]] } ]

0

Swanand的答案很棒。

如果你正在使用FactoryGirl,你可以使用它的build方法生成不带键id的属性哈希表。例如:

build(:post).attributes

0

虽然这是一个老问题,但是被广泛引用...我认为大多数人使用其他方法,但实际上有一个to_hash方法,它必须正确设置。通常,在Rails 4之后,pluck是更好的答案...我主要回答这个问题是因为我不得不搜索很多才能找到这个线程或任何有用的东西,并且假设其他人也遇到了同样的问题...

注意:不建议每个人都使用此方法,但适用于边缘情况!


从 Ruby on Rails API ... http://api.rubyonrails.org/classes/ActiveRecord/Result.html ...

This class encapsulates a result returned from calling #exec_query on any database connection adapter. For example:

result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
result # => #<ActiveRecord::Result:0xdeadbeef>

...

# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
      {"id" => 2, "title" => "title_2", "body" => "body_2"},
      ...
     ] ...

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