Użycie serializacji Railsów do zapisania skrótu do bazy danych

135

Próbuję zapisać identyfikatory mapowania skrótu do wielu prób w mojej aplikacji rails. Moja migracja do bazy danych w celu dostosowania tej nowej kolumny:

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

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

W swoim modelu mam:

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

Ale kiedy używam konsoli railsów, aby to przetestować, wykonując:

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

Wynik jest fałszywy. Co tu się dzieje?

cmwright
źródło
4
Czy jest coś w user.errors po próbie zapisania rekordu?
Martijn,
1
W przyszłości możesz użyć metody bang (zapisz!), Aby zgłosić wyjątek i wyświetlić komunikat o błędzie.
Leishman,
Najlepsza odpowiedź używa teraz stackoverflow.com/a/21397522/1536309
Blair Anderson

Odpowiedzi:

174

Typ kolumny jest nieprawidłowy. Powinieneś użyć tekstu zamiast ciągu. Dlatego Twoja migracja powinna być:

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

Następnie Railsy odpowiednio przekonwertują go na YAML (i przeprowadzą odpowiednią serializację). Pola ciągów mają ograniczony rozmiar i będą zawierać tylko szczególnie małe wartości.

Benjamin Tan Wei Hao
źródło
1
@BenjaminTan, jaki jest tego powód, dlaczego nie mogę przechowywać skrótu w typie danych „string”.
Lohith MV
8
Ponieważ w bazie danych String ma stałą długość 255 (tak mi się wydaje). Ale jeśli miałbyś serializować skrót o rozmiarze porównawczym, z łatwością przekroczyłby to długość. To samo dotyczy tablic. Tekst pozwala na znacznie większe długości.
Benjamin Tan Wei Hao
72

AKTUALIZACJA:

Dokładna realizacja będzie zależała od bazy danych, ale teraz ma PostgreSQL jsoni jsonbkolumn, które mogą natywnie przechowywania swoich danych hash / przedmiot i pozwalają na zapytania przeciwko JSON z ActiveRecord !

zmień migrację i gotowe.

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

ORYGINALNY:

Więcej informacji: rails docs && apidock

Upewnij się, że twoja kolumna jest, :texta nie:string

Migracja:

$ rails g migration add_location_data_to_users location_data:text

powinien stworzyć:

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

Twoja klasa wyglądałaby tak:

class User < ActiveRecord::Base
  serialize :location_data
end

Możliwe akcje:

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

Więcej niesamowitych ?!

wykorzystaj hstore postgresql

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

Dzięki hstore możesz ustawić atrybuty w serializowanym polu

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end
Blair Anderson
źródło
2
Naprawdę niesamowite! Dzięki!
Alexander Gorg,
18

Rails 4 ma nową funkcję o nazwie Store , dzięki czemu możesz łatwo użyć jej do rozwiązania problemu. Możesz zdefiniować dla niego akcesor i zaleca się zadeklarowanie kolumny bazy danych używanej dla serializowanego magazynu jako tekst, aby było dużo miejsca. Oryginalny przykład:

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'
Aboozar Rajabi
źródło