Rails Объект для хеширования


148

У меня есть следующий объект, который был создан

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

Как только это будет сохранено, я хочу иметь возможность вернуть объект в хеш, например, выполнив что-то вроде:

@object.to_hash

Как это возможно изнутри рельсов?

Ответы:


297

Если вы ищете только атрибуты, вы можете получить их:

@post.attributes

Обратите внимание, что этот вызов вызывается ActiveModel::AttributeSet.to_hashкаждый раз, когда вы вызываете его, поэтому, если вам нужно обращаться к хешу несколько раз, вы должны кэшировать его в локальной переменной:

attribs = @post.attributes

30
Не используйте это при цикле, дорогой метод . Перейти с as_json
AnkitG

5
.to_jsonбудет запрашивать БД, если модель не завершена
ykhrustalev

1
Работает с joinsи select, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributesвернется { street: "", name: "" }
fangxing

4
@AnkitG Я не верю, что as_json дешевле. Если вы посмотрите на исходный код для as_json, он будет вызывать, serializable_hashкоторый, в свою очередь, будет вызывать attributes! Таким образом, ваш совет на самом деле добавляет два уровня сложности attributes, делая его еще дороже.
sandre89

2
.as_jsonбудет сериализовать объект в рубиновый хэш
roadev

45

В самой последней версии Rails (хотя не могу сказать, какая именно), вы можете использовать as_jsonметод:

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

Будет выводить:

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

Чтобы пойти немного дальше, вы можете переопределить этот метод, чтобы настроить способ отображения ваших атрибутов, выполнив что-то вроде этого:

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

Затем, с тем же экземпляром, что и выше, выведет:

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

Это, конечно, означает, что вы должны явно указать, какие атрибуты должны появляться.

Надеюсь это поможет.

РЕДАКТИРОВАТЬ :

Также вы можете проверить гем Hashifiable .


OP не просил JSON, но хэш.
Дэвид Хэмпи

5
@DavidHempy Пожалуйста, внимательно прочитайте мой ответ, прежде чем голосовать. Как показано в моих примерах, это именно то, для чего предназначен и предназначен #as_json : api.rubyonrails.org/classes/ActiveModel/Serializers/… . Я не выбрал название этого метода.
Раф

25
@object.as_json

as_json имеет очень гибкий способ конфигурировать сложный объект в соответствии с модельными отношениями

ПРИМЕР

Модельная кампания принадлежит магазину и имеет один список

У списка моделей есть много list_tasks, и у каждого из list_tasks есть много комментариев

Мы можем получить один JSON, который легко объединяет все эти данные.

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

Методы уведомления :: теги могут помочь вам прикрепить любой дополнительный объект, который не имеет отношения с другими. Вам просто нужно определить метод с тегами имен в модельной кампании . Этот метод должен возвращать все, что вам нужно (например, Tags.all)

Официальная документация для as_json


Сделал пользовательскую функцию прямо перед тем, как найти это. Требуется больше одноразового метода, а не определения функции для класса. Пропустил это даже после работы с методами сериализации XML по какой-то причине. to_Вариант , кажется, работает почти точно так же , как as_вариант, для цитируемого выхода за исключением. Единственное, что мне не понравилось, - это не сохранять порядок критериев фильтра. Вроде бы отсортировано по алфавиту. Я думал, что это связано с драгоценным камнем awesome_print, который есть в моей среде, но я не думаю, что это так.
Пизис

8

Вы можете получить атрибуты модельного объекта, возвращенного как хеш, используя либо

@post.attributes

или

@post.as_json

as_jsonпозволяет вам включать ассоциации и их атрибуты, а также указывать, какие атрибуты включать / исключать (см. документацию ). Однако, если вам нужны только атрибуты базового объекта, сравнительный анализ в моем приложении с ruby ​​2.2.3 и rails 4.2.2 демонстрирует, что это attributesтребует вдвое меньше времени, чем 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 снова вызовет запрос к базе данных, если вы запускаете вложенный ресурс с методом join
Tony Hsieh

6

Здесь есть несколько отличных предложений.

Думаю, стоит отметить, что модель ActiveRecord можно рассматривать как хеш-код:

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

Поэтому вместо генерации хеша атрибутов вы можете использовать сам объект в качестве хеша.


6

Вы можете определенно использовать атрибуты для возврата всех атрибутов, но вы можете добавить метод экземпляра в Post, вызвать его «to_hash» и заставить его возвращать данные, которые вы хотите получить в хэше. Что-то вроде

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

2

не уверен, что это то, что вам нужно, но попробуйте это в консоли ruby:

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

очевидно, он вернет вам хеш в консоли. если вы хотите вернуть хеш из метода - вместо «h» попробуйте использовать «return h.inspect», что-то похожее на:

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

Постер спрашивает о моделях ActiveRecord в Rails.
Джеффри Харрингтон


0

Ответ Свананда великолепен.

если вы используете FactoryGirl, вы можете использовать его buildметод для генерации хеша атрибута без ключа id. например

build(:post).attributes

0

Старый вопрос, но на него часто ссылаются ... Я думаю, что большинство людей используют другие методы, но есть to_hashметод, который должен быть правильно настроен. Вообще, pluck - лучший ответ после рельсов 4 ... отвечая на это главным образом потому, что мне пришлось искать группу, чтобы найти эту ветку или что-нибудь полезное, и предполагая, что другие сталкиваются с той же проблемой ...

Примечание: не рекомендую это для всех, но крайние случаи!


Из рубина на рельсы 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"},
      ...
     ] ...
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.