Rails Object to hash

148

Tengo el siguiente objeto que ha sido creado

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

Una vez guardado esto, quiero poder devolver el objeto a un hash, por ejemplo, haciendo algo como:

@object.to_hash

¿Cómo es esto posible desde los rieles?

ben morgan
fuente

Respuestas:

297

Si solo busca atributos, puede obtenerlos de la siguiente manera:

@post.attributes

Tenga en cuenta que esto llama ActiveModel::AttributeSet.to_hashcada vez que lo invoca, por lo que si necesita acceder al hash varias veces, debe almacenarlo en caché en una variable local:

attribs = @post.attributes
Swanand
fuente
30
No use esto cuando realice un bucle, método costoso . Ir con as_json
AnkitG
55
.to_jsonconsultará DB si el modelo no está completo
ykhrustalev
1
Funciona con joinsy select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributesregresará { street: "", name: "" }
fangxing
44
@AnkitG No creo que as_json sea menos costoso. Si observa el código fuente as_json, llamará a lo serializable_hashque, a su vez, llamará attributes. Por lo tanto, su consejo es agregar dos capas de complejidad attributes, lo que lo hace aún más costoso.
sandre89
2
.as_jsonserializará el objeto a ruby ​​hash
roadev
45

En la versión más reciente de Rails (aunque no puedo decir cuál exactamente), puede usar el as_jsonmétodo:

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

Saldrá:

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

Para ir un poco más lejos, puede anular ese método para personalizar la apariencia de sus atributos, haciendo algo como esto:

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

Luego, con la misma instancia que la anterior, generará:

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

Por supuesto, esto significa que debe especificar explícitamente qué atributos deben aparecer.

Espero que esto ayude.

EDITAR:

También puedes consultar la gema Hashifiable .

Raf
fuente
OP no pidió JSON, sino un hash.
David Hempy el
55
@DavidHempy Lea mi respuesta detenidamente antes de votar. Como se muestra en mis ejemplos, esto es exactamente lo que #as_json hace y está destinado a: api.rubyonrails.org/classes/ActiveModel/Serializers/… . No elegí el nombre de ese método.
Raf
25
@object.as_json

as_json tiene una forma muy flexible de configurar objetos complejos de acuerdo con las relaciones del modelo

EJEMPLO

La campaña modelo pertenece a la tienda y tiene una lista

La lista de modelos tiene muchas list_tasks y cada una de list_tasks tiene muchos comentarios

Podemos obtener un json que combina todos esos datos fácilmente.

@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 : las etiquetas pueden ayudarlo a adjuntar cualquier objeto adicional que no tenga relaciones con otros. Solo necesita definir un método con etiquetas de nombre en la campaña modelo . Este método debería devolver lo que necesite (por ejemplo, Tags.all)

Documentación oficial de as_json

Serge Seletskyy
fuente
Había hecho una función personalizada justo antes de encontrar esto. Quería más de un método de un solo uso, en lugar de definir una función para la clase. Perdí esto incluso después de trabajar con los métodos de serialización XML por alguna razón. La to_variante parece funcionar casi exactamente igual que la as_variante, excepto por el resultado citado. Lo único que no me gustó fue no preservar el orden de mis criterios de filtro. Parece estar ordenado alfabéticamente. Pensé que estaba relacionado con la gema awesome_print que tengo en mi entorno, pero no creo que ese sea el caso.
Pysis
8

Puede obtener los atributos de un objeto modelo devueltos como un hash utilizando

@post.attributes

o

@post.as_json

as_jsonle permite incluir asociaciones y sus atributos, así como especificar qué atributos incluir / excluir (consulte la documentación ). Sin embargo, si solo necesita los atributos del objeto base, la evaluación comparativa en mi aplicación con ruby ​​2.2.3 y rails 4.2.2 demuestra que attributesrequiere menos de la mitad de tiempo 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
fuente
as_json volverá a llamar a la consulta de la base de datos, si está ejecutando recursos anidados con join metho
Tony Hsieh
6

Aquí hay algunas sugerencias geniales.

Creo que vale la pena señalar que puede tratar un modelo ActiveRecord como un hash de esta manera:

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

Por lo tanto, en lugar de generar un hash de los atributos, puede usar el propio objeto como un hash.

Jeffrey Harrington
fuente
6

Definitivamente podría usar los atributos para devolver todos los atributos, pero podría agregar un método de instancia a Post, llamarlo "to_hash" y hacer que devuelva los datos que desea en un hash. Algo como

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

No estoy seguro de si eso es lo que necesita, pero intente esto en la consola ruby:

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

obviamente te devolverá un hash en la consola. si desea devolver un hash desde un método, en lugar de simplemente "h", intente usar "return h.inspect", algo similar 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
fuente
Poster pregunta sobre los modelos ActiveRecord en Rails.
Jeffrey Harrington
2

Mi solución:

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

La respuesta de Swanand es genial.

Si está utilizando FactoryGirl, puede utilizar su buildmétodo para generar el hash de atributo sin la clave id. p.ej

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

Antigua pregunta, pero muy referenciada ... Creo que la mayoría de la gente usa otros métodos, pero de hecho hay un to_hash método, debe configurarse correctamente. En general, desplumar es una mejor respuesta después de los rieles 4 ... respondiendo esto principalmente porque tuve que buscar un montón para encontrar este hilo o algo útil y asumiendo que otros están enfrentando el mismo problema ...

Nota: ¡no recomiendo esto para todos, sino casos extremos!


Desde el api ruby ​​on rails ... 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
fuente