Railsy Object to hash

147

Mam następujący obiekt, który został utworzony

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

Po zapisaniu chcę mieć możliwość przywrócenia obiektu do skrótu, np. Wykonując coś takiego:

@object.to_hash

Jak to możliwe z poziomu szyn?

ben morgan
źródło

Odpowiedzi:

295

Jeśli szukasz samych atrybutów, możesz je zdobyć:

@post.attributes
Swanand
źródło
30
Nie używaj tego podczas pętli, droga metoda . Idź z as_json
AnkitG,
5
.to_json
zapyta
1
Działa z joinsi select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributespowróci { street: "", name: "" }
fangxing
3
@AnkitG Nie wierzę, że as_json jest tańszy. Jeśli spojrzysz na kod źródłowy as_json, wywoła on, serializable_hashktóry z kolei zadzwoni attributes! Więc twoja rada polega na dodaniu dwóch warstw złożoności attributes, co czyni go jeszcze droższym.
sandre89
2
.as_jsonzserializuje obiekt do ruby ​​hash
roadev
45

W najnowszej wersji Railsów (nie można jednak powiedzieć, która dokładnie), możesz użyć as_jsonmetody:

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

Wyświetli:

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

Aby przejść dalej, możesz zastąpić tę metodę, aby dostosować sposób wyświetlania atrybutów, wykonując coś takiego:

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

Następnie, w tym samym wystąpieniu, co powyżej, wyświetli:

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

Oznacza to oczywiście, że musisz wyraźnie określić, które atrybuty muszą się pojawić.

Mam nadzieję że to pomoże.

EDYTOWAĆ :

Możesz również sprawdzić klejnot Hashifiable .

Raf
źródło
OP nie pytał o JSON, ale o hash.
David Hempy,
5
@DavidHempy Przed przystąpieniem do głosowania w dół dokładnie przeczytaj moją odpowiedź. Jak pokazałem w moich przykładach, właśnie to robi #as_json i jest przeznaczone do: api.rubyonrails.org/classes/ActiveModel/Serializers/… . Nie wybrałem nazwy tej metody.
Raf
25
@object.as_json

as_json ma bardzo elastyczny sposób konfigurowania złożonych obiektów zgodnie z relacjami modelu

PRZYKŁAD

Modelowa kampania należy do sklepu i ma jedną listę

Lista modeli ma wiele list_tasks, a każde z list_tasks ma wiele komentarzy

Możemy uzyskać jeden plik json, który łatwo łączy wszystkie te dane.

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

Ogłoszenie o metody:: tagi mogą pomóc dołączyć żadnego dodatkowego przedmiotu, który nie ma relacji z innymi. Wystarczy zdefiniować metodę z tagami nazw w modelowej kampanii . Ta metoda powinna zwrócić wszystko, czego potrzebujesz (np. Tags.all)

Oficjalna dokumentacja dla as_json

Serge Seletskyy
źródło
Stworzył niestandardową funkcję tuż przed znalezieniem tego. Potrzebowałem więcej metody jednorazowego użytku, zamiast definiować funkcję dla klasy. Z jakiegoś powodu przegapiłem to nawet po pracy z metodami serializacji XML. to_Wariant wydaje się działać niemal dokładnie taki sam jak as_wariant, z wyjątkiem cytowanego wyjściu. Jedyne, co mi się nie podobało, to brak zachowania kolejności moich kryteriów filtrowania. Wydaje się, że jest posortowany alfabetycznie. Myślałem, że jest to związane z klejnotem awesome_print, który mam w swoim środowisku, ale nie sądzę, że tak jest.
Pysis
8

Możesz uzyskać atrybuty obiektu modelu zwrócone jako skrót za pomocą jednego z nich

@post.attributes

lub

@post.as_json

as_jsonumożliwia dołączenie asocjacji i ich atrybutów, a także określenie, które atrybuty należy uwzględnić / wykluczyć (zobacz dokumentację ). Jeśli jednak potrzebujesz tylko atrybutów obiektu podstawowego, testy porównawcze w mojej aplikacji z Ruby 2.2.3 i railsami 4.2.2 pokazują, że attributeszajmuje to mniej niż połowę czasu 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
źródło
as_json ponownie wywoła zapytanie do bazy danych, jeśli korzystasz z zagnieżdżonych zasobów z metołączeniem
Tony Hsieh
6

Jest tutaj kilka świetnych sugestii.

Myślę, że warto zauważyć, że model ActiveRecord można potraktować jako hash w ten sposób:

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

Dlatego zamiast generować skrót atrybutów, możesz użyć samego obiektu jako skrótu.

Jeffrey Harrington
źródło
6

Zdecydowanie możesz użyć atrybutów, aby zwrócić wszystkie atrybuty, ale możesz dodać metodę instancji do Post, nazwać ją „to_hash” i poprosić o zwrócenie danych, które chcesz w haszu. Coś jak

def to_hash
 { name: self.name, active: true }
end
Curtis Edmond
źródło
2

nie jestem pewien, czy tego potrzebujesz, ale spróbuj tego w konsoli Ruby:

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

oczywiście zwróci ci hash w konsoli. jeśli chcesz zwrócić hash z wnętrza metody - zamiast po prostu "h", spróbuj użyć "return h.inspect", coś podobnego do:

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
źródło
Plakat pyta o modele ActiveRecord w Railsach.
Jeffrey Harrington
2

Moje rozwiązanie:

Hash[ post.attributes.map{ |a| [a, post[a]] } ]
fayvor
źródło
0

Odpowiedź Swananda jest świetna.

jeśli używasz FactoryGirl, możesz użyć jego buildmetody do wygenerowania skrótu atrybutu bez klucza id. na przykład

build(:post).attributes
Siwei Shen 申思维
źródło
0

Stare pytanie, ale z dużą ilością odniesień ... Myślę, że większość ludzi używa innych metod, ale istnieje to_hashmetoda, która musi być poprawnie ustawiona. Ogólnie rzecz biorąc, pluck jest lepszą odpowiedzią po szynach 4 ... odpowiadając na to głównie dlatego, że musiałem przeszukać grupę, aby znaleźć ten wątek lub cokolwiek przydatnego i zakładając, że inni mają ten sam problem ...

Uwaga: nie polecam tego wszystkim, ale skrajne przypadki!


Z ruby ​​na 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"},
      ...
     ] ...
Mirv - Matt
źródło