Objet Rails à hacher

148

J'ai l'objet suivant qui a été créé

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

Une fois que cela est enregistré, je veux être en mesure de récupérer l'objet dans un hachage, par exemple en faisant quelque chose comme:

@object.to_hash

Comment est-ce possible depuis les rails?

Ben Morgan
la source

Réponses:

297

Si vous recherchez uniquement des attributs, vous pouvez les obtenir par:

@post.attributes

Notez que cela appelle ActiveModel::AttributeSet.to_hashchaque fois que vous l'invoquez, donc si vous devez accéder au hachage plusieurs fois, vous devez le mettre en cache dans une variable locale:

attribs = @post.attributes
Cygne
la source
30
Ne l'utilisez pas lors de la boucle, méthode coûteuse . Aller avec as_json
AnkitG
5
.to_jsoninterrogera la base de données si le modèle n'est pas complet
ykhrustalev
1
Fonctionne avec joinset select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributesreviendra { street: "", name: "" }
fangxing
4
@AnkitG Je ne crois pas qu'as_json soit moins cher. Si vous regardez le code source de as_json, il appellera serializable_hashqui, à son tour, appellera attributes! Donc, votre conseil ajoute en fait deux couches de complexité attributes, ce qui le rend encore plus cher.
sandre89
2
.as_jsonsérialisera l'objet en ruby ​​hash
roadev
45

Dans la version la plus récente de Rails (je ne peux pas dire laquelle exactement), vous pouvez utiliser la as_jsonméthode:

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

Sortira:

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

Pour aller un peu plus loin, vous pouvez remplacer cette méthode afin de personnaliser la façon dont vos attributs apparaissent, en faisant quelque chose comme ceci:

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

Ensuite, avec la même instance que ci-dessus, affichera:

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

Cela signifie bien sûr que vous devez spécifier explicitement les attributs qui doivent apparaître.

J'espère que cela t'aides.

ÉDITER :

Vous pouvez également vérifier la gemme Hashifiable .

Raf
la source
OP n'a pas demandé JSON, mais un hachage.
David Hempy
5
@DavidHempy Veuillez lire attentivement ma réponse avant de voter. Comme je l'ai montré dans mes exemples, c'est exactement ce que fait #as_json et est destiné à: api.rubyonrails.org/classes/ActiveModel/Serializers/… . Je n'ai pas choisi le nom de cette méthode.
Raf
25
@object.as_json

as_json a un moyen très flexible de configurer des objets complexes en fonction des relations de modèle

EXEMPLE

La campagne modèle appartient à la boutique et a une liste

La liste de modèles comporte de nombreuses list_tasks et chacune des list_tasks contient de nombreux commentaires

Nous pouvons obtenir un json qui combine toutes ces données facilement.

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

Remarquez les méthodes:: les balises peuvent vous aider à attacher tout objet supplémentaire qui n'a pas de relations avec d'autres. Il vous suffit de définir une méthode avec des tags de nom dans la campagne modèle . Cette méthode devrait renvoyer tout ce dont vous avez besoin (par exemple, Tags.all)

Documentation officielle pour as_json

Serge Seletskyy
la source
Avait créé une fonction personnalisée juste avant de trouver cela. Je voulais plus d'une méthode à usage unique, plutôt que de définir une fonction pour la classe. J'ai manqué cela même après avoir travaillé avec les méthodes de sérialisation XML pour une raison quelconque. La to_variante semble fonctionner presque exactement de la même manière que la as_variante, à l'exception de la sortie citée. La seule chose que je n'aimais pas, c'était de ne pas conserver l'ordre de mes critères de filtre. Il semble être trié par ordre alphabétique. Je pensais que c'était lié à la gemme awesome_print que j'ai dans mon environnement, mais je ne pense pas que ce soit le cas.
Pysis
8

Vous pouvez obtenir les attributs d'un objet de modèle renvoyés sous forme de hachage en utilisant soit

@post.attributes

ou

@post.as_json

as_jsonvous permet d'inclure des associations et leurs attributs ainsi que de spécifier les attributs à inclure / exclure (voir documentation ). Cependant, si vous n'avez besoin que des attributs de l'objet de base, l'analyse comparative dans mon application avec ruby ​​2.2.3 et rails 4.2.2 montre que cela prend attributesmoins de la moitié du temps 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
la source
as_json appellera à nouveau la requête de base de données, si vous exécutez une ressource imbriquée avec join metho
Tony Hsieh
6

Il y a de bonnes suggestions ici.

Je pense qu'il vaut la peine de noter que vous pouvez traiter un modèle ActiveRecord comme un hachage comme ceci:

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

Par conséquent, au lieu de générer un hachage des attributs, vous pouvez utiliser l'objet lui-même comme hachage.

Jeffrey Harrington
la source
6

Vous pouvez certainement utiliser les attributs pour renvoyer tous les attributs, mais vous pouvez ajouter une méthode d'instance à Post, l'appeler "to_hash" et lui faire renvoyer les données que vous souhaitez dans un hachage. Quelque chose comme

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

Je ne sais pas si c'est ce dont vous avez besoin, mais essayez ceci dans la console ruby:

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

évidemment il vous renverra un hash en console. si vous voulez renvoyer un hachage à partir d'une méthode - au lieu de simplement "h" essayez d'utiliser "return h.inspect", quelque chose de similaire à:

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
la source
Poster pose des questions sur les modèles ActiveRecord dans Rails.
Jeffrey Harrington
2

Ma solution:

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

La réponse de Swanand est excellente.

si vous utilisez FactoryGirl, vous pouvez utiliser sa buildméthode pour générer le hachage d'attribut sans la clé id. par exemple

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

Ancienne question, mais fortement référencée ... Je pense que la plupart des gens utilisent d'autres méthodes, mais il y a en fait une to_hashméthode, elle doit être configurée correctement. Généralement, pluck est une meilleure réponse après les rails 4 ... répondant à cela principalement parce que j'ai dû chercher un tas pour trouver ce fil ou quelque chose d'utile et en supposant que les autres rencontrent le même problème ...

Remarque: ne pas recommander cela à tout le monde, mais des cas extrêmes!


Depuis l'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
la source