Objeto do Rails para hash

147

Eu tenho o seguinte objeto que foi criado

@post = Post.create(:name => 'test', :post_number => 20, :active => true)

Depois que isso for salvo, quero poder recuperar o objeto para um hash, por exemplo, fazendo algo como:

@object.to_hash

Como isso é possível de dentro dos trilhos?

Ben Morgan
fonte

Respostas:

295

Se você estiver procurando apenas atributos, poderá obtê-los:

@post.attributes
Swanand
fonte
30
Não use isso ao fazer um loop, método caro . Vá com as_json
AnkitG
5
.to_jsonconsultará o banco de dados se o modelo não estiver completo
ykhrustalev
1
Funciona com joinse select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributes, vai voltar { street: "", name: "" }
Fangxing
3
@AnkitG Não acredito que as_json seja menos caro. Se você procurar o código fonte as_json, ele chamará o serializable_hashque, por sua vez, chamará attributes! Portanto, seu conselho é realmente adicionar duas camadas de complexidade attributes, tornando-a ainda mais cara.
17268 sandre89
2
.as_jsonserializará o objeto para ruby ​​hash
roadev 15/03/19
45

Na versão mais recente do Rails (não é possível saber exatamente qual), você pode usar o as_jsonmétodo:

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

Saída:

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

Para ir um pouco mais longe, você pode substituir esse método para personalizar a aparência dos seus atributos, fazendo algo assim:

class Post < ActiveRecord::Base
  def as_json(*args)
    {
      :name => "My name is '#{self.name}'",
      :post_number => "Post ##{self.post_number}",
    }
  end
end

Em seguida, com a mesma instância acima, será exibida:

{ 
  :name => "My name is 'test'",
  :post_number => "Post #20"
}

É claro que isso significa que você deve especificar explicitamente quais atributos devem aparecer.

Espero que isto ajude.

EDIT:

Além disso, você pode verificar a gema Hashifiable .

Raf
fonte
O OP não pediu JSON, mas um hash.
David Hempy
5
@DavidHempy Por favor, leia minha resposta completamente antes de votar. Como mostrado nos meus exemplos, é exatamente isso que o #as_json faz e se destina: api.rubyonrails.org/classes/ActiveModel/Serializers/… . Eu não escolhi o nome desse método.
Raf
25
@object.as_json

o as_json possui uma maneira muito flexível de configurar objetos complexos de acordo com as relações do modelo

EXEMPLO

A campanha modelo pertence à loja e tem uma lista

A lista de modelos possui muitas list_tasks e cada list_tasks possui muitos comentários

Podemos obter um json que combina todos esses dados facilmente.

@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
    })

Métodos de aviso :: tags podem ajudá-lo a anexar qualquer objeto adicional que não tenha relações com outras pessoas. Você só precisa definir um método com tags de nome na campanha modelo . Este método deve retornar o que você precisar (por exemplo, Tags.all)

Documentação oficial para as_json

Serge Seletskyy
fonte
Tinha feito uma função personalizada pouco antes de encontrar isso. Queria mais um método de uso único, em vez de definir uma função para a classe. Perdeu isso mesmo depois de trabalhar com os métodos de serialização XML por algum motivo. A to_variante parece funcionar quase exatamente da mesma forma que a as_variante, exceto pela saída citada. A única coisa que eu não gostei foi não preservar a ordem dos meus critérios de filtro. Parece estar em ordem alfabética. Eu pensei que isso estivesse relacionado à gema awesome_print que eu tenho no meu ambiente, mas não acho que seja esse o caso.
Pysis
8

Você pode obter os atributos de um objeto de modelo retornados como um hash usando

@post.attributes

ou

@post.as_json

as_jsonpermite incluir associações e seus atributos, bem como especificar quais atributos incluir / excluir (consulte a documentação ). No entanto, se você precisar apenas dos atributos do objeto base, o benchmarking no meu aplicativo com o ruby ​​2.2.3 e o rails 4.2.2 demonstra que isso attributesrequer menos da metade do tempo 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)
John Cole
fonte
as_json vai chamar a consulta de banco de dados, novamente, se você estiver executando resouce aninhada com juntar meto
Tony Hsieh
6

Existem algumas ótimas sugestões aqui.

Eu acho que vale a pena notar que você pode tratar um modelo ActiveRecord como um hash assim:

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

Portanto, em vez de gerar um hash dos atributos, você pode usar o próprio objeto como um hash.

Jeffrey Harrington
fonte
6

Você definitivamente poderia usar os atributos para retornar todos os atributos, mas poderia adicionar um método de instância ao Post, chamá-lo de "to_hash" e fazer com que ele retornasse os dados que você gostaria em um hash. Algo como

def to_hash
 { name: self.name, active: true }
end
Curtis Edmond
fonte
2

não tenho certeza se é isso que você precisa, mas tente isso no console ruby:

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

Obviamente, ele retornará um hash no console. se você deseja retornar um hash de dentro de um método - em vez de apenas "h", tente usar "return h.inspect", algo semelhante a:

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
Yuri
fonte
O pôster está perguntando sobre os modelos do ActiveRecord no Rails.
Jeffrey Harrington
2

Minha solução:

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

A resposta de Swanand é ótima.

se você estiver usando o FactoryGirl, poderá usar seu buildmétodo para gerar o hash do atributo sem a chave id. por exemplo

build(:post).attributes
Siwei Shen 申思维
fonte
0

Pergunta antiga, mas muito referenciada ... Eu acho que a maioria das pessoas usa outros métodos, mas existe um to_hashmétodo de fato, ele deve ser configurado corretamente. Geralmente, arrancar é uma resposta melhor após os trilhos 4 ... respondendo a isso principalmente porque eu tive que procurar um monte para encontrar esse tópico ou qualquer coisa útil e assumindo que outros estão enfrentando o mesmo problema ...

Nota: não recomendamos isso para todos, mas para casos extremos!


Api do rubi nos trilhos ... 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"},
      ...
     ] ...
Mirv - Matt
fonte