Использование Rails для сериализации, чтобы сохранить хэш в базе данных

135

Я пытаюсь сохранить хэш-идентификаторы для нескольких попыток в моем приложении rails. Моя миграция в базу данных для размещения этого нового столбца:

class AddMultiWrongToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :multi_wrong, :string
  end

  def self.down
    remove_column :users, :multi_wrong
  end
end

В моей модели у меня есть:

class User < ActiveRecord::Base 
 serialize :multi_wrong, Hash
end

Но когда я использую консоль rails, чтобы проверить это, выполните:

user = User.create()
user.multi_wrong = {"test"=>"123"}
user.save

Выход ложный. Что здесь не так?

cmwright
источник
4
Есть ли что-то в user.errors после попытки сохранить запись?
Мартейн
1
В будущем вы можете использовать метод взрыва (сохранить!), Чтобы вызвать исключение и отобразить сообщение об ошибке.
лейшман
Лучший ответ теперь использует столбец JSON stackoverflow.com/a/21397522/1536309
Блэр Андерсон

Ответы:

174

Тип столбца неправильный. Вы должны использовать текст вместо строки. Следовательно, ваша миграция должна быть:

 def self.up
   add_column :users, :multi_wrong, :text
 end

Тогда Rails будет правильно конвертировать его в YAML для вас (и выполнить правильную сериализацию). Строковые поля ограничены по размеру и будут содержать только особо малые значения.

Бенджамин Тан Вэй Хао
источник
1
@BenjaminTan, что является причиной этого, почему я не могу хранить хэш в типе данных 'string'.
Lohith MV
8
Потому что в базе данных String имеет фиксированную длину 255 (я думаю). Но если бы вы сериализовали хеш сравнительного размера, это легко превысило бы его длину. То же самое для массивов. Текст допускает гораздо большую длину.
Бенджамин Тан Вэй Хао
72

ОБНОВЛЕНО:

Точная реализация будет зависеть от вашей базы данных, но в PostgreSQL теперь есть jsonи jsonbстолбцы, которые могут хранить ваши хеш / объектные данные и позволять вам выполнять запросы к JSON с ActiveRecord !

измените свою миграцию, и все готово.

class Migration0001
  def change
    add_column :users, :location_data, :json, default: {}
  end
end

ОРИГИНАЛ:

Больше подробностей: rails docs && apidock

Убедитесь, что ваш столбец есть, :textа не:string

Миграция:

$ rails g migration add_location_data_to_users location_data:text

должен создать:

class Migration0001
  def change
    add_column :users, :location_data, :text
  end
end

Ваш класс будет выглядеть так:

class User < ActiveRecord::Base
  serialize :location_data
end

Доступные действия:

b = User.new
b.location_data = [1,2,{foot: 3, bart: "noodles"}]
b.save

Более круто ?!

использовать postgresql hstore

class AddHstore < ActiveRecord::Migration  
  def up
    enable_extension :hstore
  end

  def down
    disable_extension :hstore
  end
end 

class Migration0001
  def change
    add_column :users, :location_data, :hstore
  end
end

С hstore вы можете установить атрибуты в сериализованном поле

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end
Блэр Андерсон
источник
2
Действительно круто! Спасибо!
Александр Горг
18

В Rails 4 появилась новая функция, которая называется Store , поэтому вы можете легко использовать ее для решения вашей проблемы. Вы можете определить для него метод доступа, и рекомендуется объявить столбец базы данных, используемый для сериализованного хранилища, в виде текста, так что места достаточно. Оригинальный пример:

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ], coder: JSON
end

u = User.new(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor

# There is no difference between strings and symbols for accessing custom attributes
u.settings[:country]  # => 'Denmark'
u.settings['country'] # => 'Denmark'
Абузар Раджаби
источник