Rails: Serializing objects in a database?
In computer science, in the context of data storage and transmission, serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file, a memory buffer, or transmitted across a network connection link to be "resurrected" later in the same or another computer environment.
(see http://en.wikipedia.org/wiki/Serialization)
So serialized objects (in the context of ActiveRecord) are text/string representations of objects (encoded using YAML). When serialized, you can save (almost) any Ruby object in a single database field.
You can use serialization if you have somewhat complex objects that you need to save in a database and you don't need to retrieve records based on the contents of a serialized attribute. I used them for example for storing preferences for users of a webapp: the preferences were basically hashes that I wanted to save in a single db field.
3./4./5. Use ActiveRecord::Base.serialize as Marc-André Lafortune suggested:
class User < ActiveRecord::Base
serialize :preferences
end
u = User.new
u.preferences = {:show_tooltips => true, :use_extended_menu => false, ...}
u.save
# ...
u = User.find(23)
u.preferences # => {:show_tooltips => true, :use_extended_menu => false, ...}
Serializing ActiveRecord objects without storing their attributes?
As far as I understand your problem you should google for: acts_as_nested_set
better_nested_set
even_better_nested_set
and awesome_nested_set
. I know they don't store the serialized hierarchies, but you should store their nodes atomically. Even huge traffic sites do that. Other than that - you should consider NoSQL (or schema-less DB).
Why is my object getting serialized into a string not text in my Rails ActiveRecord DB?
I see in your source code:
class Query
def initialize
@client = Soundcloud.new(:client_id => API_KEY)
end
def query_user(user_url)
@user_url = user_url
@user = @client.get('/resolve', :url => @user_url)
end
end
At this point @user
is a still a SoundCloud::HashResponseWrapper
object.
If you wanna store the data as string text you'll need to first do:
@user.to_json
To save it with symbolized keys in the db:
data = JSON.parse(@user.to_json).symbolize_keys
sc_user = SoundcloudUser.new(user_hash: data)
But you still can't call .key
on a hash. If you want to call the value of the key you'll need to do:
sc_user.user_hash[:username] #for example
There are ways to extend your model but that's out of scope for this question.
UPDATE: Here's your updated controller:
class SoundcloudQueriesController < ApplicationController
def new
end
def index
@user = SoundcloudUser.find(3)
@user_hash = @user.user_hash # but do you even need this?
end
def create
sc_user_data = Query.new.get_user params[:username]
@soundcloud_user = SoundcloudUser.create({
user_name: sc_user_data[:username],
user_hash: JSON.parse(sc_user_data.to_json).symbolize_keys
})
end
def show
end
end
You'll also need to modify your Query class to be able to build a user from the souncloud username. It will look like this:
class Query
BASE_RESOLVE_URL="http://soundcloud.com/"
def initialize
@client = Soundcloud.new :client_id => ENV['SC_CLIENT_ID']
end
def query_user(user_url)
@user_url = user_url
@user = @client.get('/resolve', :url => @user_url)
end
def get_user(username)
@client.get('/resolve', url: BASE_RESOLVE_URL+username)
end
end
I'm not exactly sure what you want to use the query_user
method for, or if you can just remove it and use get_user
instead. In any case I forked your repo, got the controller create test passing. Will send PR.
Deserialized Rails ActiveRecord object won't save to SQL database
Solved: Calling var_name.dup.save
works.
Intermediate debugging steps that might be helpful:
[6] pry(main)> fpt.valid?
=> true
[7] pry(main)> fpt.changed?
=> false
[8] pry(main)> fpt.save!
=> true
[9] pry(main)> FakeProductTest.count
=> 0
[11] pry(main)> fpt.errors.messages
=> {}
Thanks to @thorncp
How to add an element into an array of a Serialize Field using Ruby on Rails
Base on @veridian-dynamics (thanks for your help!) Here what I did.
Model:
class MyModel < ApplicationRecord
serialize :item_data, JSON
end
Controller:
class ItemController < ApplicationController
before_action :authenticate_user!
def add_item
begin
mymodel = MyModel.find_or_create_by(id: param[:model_id])
if mymodel .item_data.blank?
item = {:items => []}
else
item = mymodel.item_data.deep_symbolize_keys
end
bookmark_exist = item[:items].any? {|i| i[:id] == params[:id]}
if !bookmark_exist
item[:items] = item[:items ].append({id: params[:id]}) # adding a new item
end
mymodel.item_data = item
mymodel.save
return render :json => item, :status=> 200
rescue Exception => e
return render :json =>{:errors=>e.message}, :status=> 400
puts "ERROR: #{e.message}"
end
end
def delete_item
begin
mymodel = MyModel.find_by(id: params[:model_id])
if mymodel.present? && mymodel.item_data.present?
item = mymodel.item_data.deep_symbolize_keys
item[:items] = (item[:items].select { |itm| itm[:id] != params[:id] }) # remove an item
mymodel.item_data = item
mymodel.save
return render :json => item, :status=> 200
end
rescue Exception => e
return render :json =>{:errors=>e.message}, :status=> 400
puts "ERROR: #{e.message}"
end
end
end
Rails can't write to serialized array field in database
If you're using native PostgreSQL arrays (which you are since you have array: true
in your migration) then you shouldn't use serialize
at all. serialize
is used to store YAML in the database:
serialize(attr_name, class_name_or_coder = Object)
If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, then specify the name of that attribute using this method and it will be handled automatically. The serialization is done through YAML. If
class_name
is specified, the serialized object must be of that class on assignment and retrieval. OtherwiseSerializationTypeMismatch
will be raised.
So serialize
simply stores a YAML-encoded object inside a text
column in the database. But PostgreSQL, ActiveRecord in Rails4, and the underlying PostgreSQL driver all understands arrays without all the YAML unpleasantness.
Leave the array: true
in your migration, remove the serialize :content, Array
from your model, and it should work fine. As an added bonus, you'll be able to use all of PostgreSQL's array operators and functions to query your content
, serialize
doesn't allow you to do this.
De-serializing rails database array
It looks like you have some stray data in there which is mucking things up. The first thing that's inserted into the block is '1', and then when '1'.username is called your error is thrown. You can sidestep this issue by skipping anything that's not a User:
<ul class="list-group">
<% @user.company.members.each do |m| %>
<% break unless m.class == User %>
<li class="list-group-item"><%= m.username %></li>
<% end %>
</ul>
But that's a really ugly solution and you should probably figure out why you have stray data polluting your database in the first place. I think what you might actually be trying to do is use a has_many/belongs_to association to tie Users to a Company. Instead of using serialize, rails can handle all of the gruntwork for you.
Related Topics
Rails Generate Has_Many Association
Which Ruby Memoize Pattern Does Activesupport::Memoizable Refer To
Catching Line Numbers in Ruby Exceptions
Respond_To' VS. 'Respond_To_Missing'
How to Check from Ruby Whether a Process with a Certain Pid Is Running
How to Require a Specific Version of a Ruby Gem
How to Get the Width of Terminal Window in Ruby
How to Sort a Hash by Value in Descending Order and Output a Hash in Ruby
Ruby - Bundle Install/Update Too Slow
Can't Use Compass After Installing It
Ruby: Select a Hash from Inside an Array
Instance and Class Variables in Rails Controller
An Error Occurred While Installing Debugger-Linecache (1.1.1), and Bundler Cannot Continue
How to Define_Method in Rails Models
Best Way to Generate Order Numbers for an Online Store
Ruby Singleton Methods with (Class_Eval, Define_Method) VS (Instance_Eval, Define_Method)