Rails Oggetto dell'hash


148

Ho il seguente oggetto che è stato creato

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

Una volta salvato questo, voglio essere in grado di riportare l'oggetto in un hash, ad esempio facendo qualcosa del tipo:

@object.to_hash

Come è possibile all'interno delle rotaie?

Risposte:


297

Se stai cercando solo attributi, puoi ottenerli tramite:

@post.attributes

Si noti che questo chiama ActiveModel::AttributeSet.to_hashogni volta che lo si richiama, quindi se è necessario accedere all'hash più volte, è necessario memorizzarlo nella cache locale:

attribs = @post.attributes

30
Non utilizzarlo durante il ciclo, metodo costoso . Vai con as_json
AnkitG il

5
.to_jsoninterrogherà DB se il modello non è completo
ykhrustalev

1
Funziona con joinse select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributestornerà { street: "", name: "" }
fangxing il

4
@AnkitG Non credo che as_json sia meno costoso. Se guardi il codice sorgente per as_json, chiamerà serializable_hashche, a sua volta, chiamerà attributes! Quindi il tuo consiglio in realtà sta aggiungendo due livelli di complessità attributes, rendendolo ancora più costoso.
sandre89,

2
.as_jsonserializzerà l'oggetto su ruby ​​hash
roadev

45

Nella versione più recente di Rails (non si può dire quale esattamente esattamente però), è possibile utilizzare il as_jsonmetodo:

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

Uscita:

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

Per andare un po 'oltre, potresti ignorare quel metodo al fine di personalizzare il modo in cui i tuoi attributi appaiono, facendo qualcosa del genere:

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

Quindi, con la stessa istanza di cui sopra, verrà generato:

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

Questo ovviamente significa che devi specificare esplicitamente quali attributi devono apparire.

Spero che questo ti aiuti.

MODIFICARE :

Inoltre puoi controllare la gemma Hashifiable .


OP non ha richiesto JSON, ma un hash.
David Hempy,

5
@DavidHempy Per favore leggi attentamente la mia risposta prima di effettuare il downgrade. Come mostrato nei miei esempi, questo è esattamente ciò che fa #as_json ed è destinato a: api.rubyonrails.org/classes/ActiveModel/Serializers/… . Non ho scelto il nome di quel metodo.
Raf,

25
@object.as_json

as_json ha un modo molto flessibile di configurare oggetti complessi in base alle relazioni del modello

ESEMPIO

La campagna modello appartiene allo shop e ha un elenco

L' elenco dei modelli ha molti list_tasks e ognuno di list_tasks ha molti commenti

Possiamo ottenere un json che combina facilmente tutti quei dati.

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

Metodi di avviso : i tag possono aiutarti a collegare qualsiasi oggetto aggiuntivo che non ha relazioni con gli altri. Devi solo definire un metodo con le etichette nome nella campagna modello . Questo metodo dovrebbe restituire tutto ciò di cui hai bisogno (ad esempio Tags.all)

Documentazione ufficiale per as_json


Aveva creato una funzione personalizzata prima di trovarla. Volevo più di un metodo monouso, piuttosto che definire una funzione per la classe. Perso questo anche dopo aver lavorato con i metodi di serializzazione XML per qualche motivo. La to_variante sembra funzionare quasi esattamente come la as_variante, ad eccezione dell'output citato. L'unica cosa che non mi è piaciuta non è stata preservare l'ordine dei miei criteri di filtro. Sembra essere in ordine alfabetico. Ho pensato che fosse legato alla gemma awesome_print che ho nel mio ambiente, ma non penso che sia così.
Pysis,

8

Puoi ottenere gli attributi di un oggetto modello restituiti come hash usando entrambi

@post.attributes

o

@post.as_json

as_jsonconsente di includere le associazioni e i relativi attributi, nonché di specificare quali attributi includere / escludere (consultare la documentazione ). Tuttavia, se hai solo bisogno degli attributi dell'oggetto base, il benchmarking nella mia app con ruby ​​2.2.3 e rails 4.2.2 dimostra che attributesrichiede meno della metà del 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)

as_json chiamerà di nuovo la query del database, se stai eseguendo una risorsa nidificata con join metho
Tony Hsieh,

6

Ci sono alcuni ottimi suggerimenti qui.

Penso che valga la pena notare che è possibile trattare un modello ActiveRecord come un hash in questo modo:

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

Pertanto, anziché generare un hash degli attributi, è possibile utilizzare l'oggetto stesso come hash.


6

Potresti sicuramente usare gli attributi per restituire tutti gli attributi, ma potresti aggiungere un metodo di istanza a Post, chiamarlo "to_hash" e farlo restituire i dati che desideri in un hash. Qualcosa di simile a

def to_hash
 { name: self.name, active: true }
end

2

non sono sicuro se è quello che ti serve, ma prova questo nella console Ruby:

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

ovviamente ti restituirà un hash in console. se vuoi restituire un hash dall'interno di un metodo - invece di semplicemente "h" prova a usare "return h.inspect", qualcosa di simile 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

Poster chiede informazioni sui modelli ActiveRecord in Rails.
Jeffrey Harrington,

2

La mia soluzione:

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

0

La risposta di Swanand è ottima.

se stai usando FactoryGirl, puoi usare il suo buildmetodo per generare l'hash dell'attributo senza la chiave id. per esempio

build(:post).attributes

0

Vecchia domanda, ma fortemente referenziata ... Penso che la maggior parte delle persone usi altri metodi, ma esiste un to_hashmetodo, deve essere impostato correttamente. In generale, pluck è una risposta migliore dopo le rotaie 4 ... rispondendo principalmente perché ho dovuto cercare un gruppo per trovare questa discussione o qualcosa di utile e supponendo che gli altri stiano colpendo lo stesso problema ...

Nota: non lo consiglio a tutti, ma ai casi limite!


Dal rubino su rotaia 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"},
      ...
     ] ...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.