I am currently upgrading a Ruby on Rails app from 4.2 to 5.0 and am running into a roadblock concerning fields that store data as a serialized hash. For instance, I have
class Club
serialize :social_media, Hash
end
When creating new clubs and inputting the social media everything works fine, but for the existing social media data I'm getting:
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Hash, but was a ActionController::Parameters.
How can I convert all of the existing data from ActionController::Parameter
objects to simple hashes? Database is mysql.
The official Ruby on Rails documentation has a section about upgrading between Rails versions that explains more about the error you have:
From the fine manual:
So when you say this:
ActiveRecord will require the unserialized
social_media
to be aHash
. However, as noted by vnbrs,ActionController::Parameters
no longer subclassesHash
like it used to and you have a table full of serializedActionController::Parameters
instances. If you look at the raw YAML data in yoursocial_media
column, you'll see a bunch of strings like:rather than Hashes like this:
You should fix up all your existing data to have YAMLized Hashes in
social_media
rather thanActionController::Parameters
and whatever else is in there. This process will be somewhat unpleasant:social_media
out of the table as a string.obj = YAML.load(str)
.h = obj.to_unsafe_h
.str = h.to_yaml
.Note the
to_unsafe_h
call in (3). Just callingto_h
(orto_hash
for that matter) on anActionController::Parameters
instance will give you an exception in Rails5, you have to include apermit
call to filter the parameters first:If you use
to_unsafe_h
(orto_unsafe_hash
) then you get the whole thing in aHashWithIndifferentAccess
. Of course, if you really want a plain old Hash then you'd say:to unwrap the indifferent access wrapper as well. This also assumes that you only have
ActionController::Parameters
insocial_media
so you might need to include anobj.respond_to?(:to_unsafe_hash)
check to see how you unpack yoursocial_media
values.You could do the above data migration through direct database access in a Rails migration. This could be really cumbersome depending on how nice the low level MySQL interface is. Alternatively, you could create a simplified model class in your migration, something sort of like this:
You'd want to use
find_in_batches
orin_batches_of
insteadall
if you have a lot ofClub
s of course.If your MySQL supports
json
columns and ActiveRecord works with MySQL'sjson
columns (sorry, PostgreSQL guy here), then this might be a good time to change the column tojson
and run far away fromserialize
.