simonmorley
simonmorley

Reputation: 2804

"Attribute was supposed to be a Hash, but was a String"

I'm having problems saving a hash in my Rails 3 application. I can save it OK when using the console - it just doesn't work when I submit the hash through a form.

This SO question addresses it but the solution's not working for me. Moreover, if I use:

 serialize :bulk_action, Hash

I get the error:

 Attribute was supposed to be a Hash, but was a String

The hash, when saved through the form looks like this:

 "{\"location\"=>{\"commands\"=>{\"custom_command_one\"=>\"true\", \"custom_command_two\"=>\"true\"}}}"

Whereas, through the console:

{"location"=>{"commands"=>{"custom_command_one"=>"true", "custom_command_two"=>"true"}}}

My database field is a text field. What's the best way to go about saving a hash through a form?

-- EDIT --

I've found I can sort of get around the problem if I use symbols not strings for the names but it still outputs one long string, not the hash.

Upvotes: 4

Views: 5412

Answers (4)

loybert
loybert

Reputation: 416

I lately run over this kind of error too. The chain of causing actions were:

  • had a Model with a serializable field data
  • that field had to be present (model-validation + db-constraint)
  • because of the the db-constraint NOT NULL the sqlite db-adapter from the build-matrix was failing, because it cannot allow default-value NULL on a NOT NULL restricted field
  • because of sqlite the empty string was set as the field's default-value: default: ''
  • when initializing a new Model, it was preset with instance.data = '' as we upgraded on rails4
  • but saving this model failed with the error-message “Attribute was supposed to be a Hash, but was a String”, because there wasn't anything overwriting this faulty field-value

The solution: - add a migration with: change_column :model, :data, :text, default: '--- {}' as a valid serializable empty default-value

Upvotes: 1

Sumit Munot
Sumit Munot

Reputation: 3868

From the link http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize

serialize(attr_name, class_name = Object)
[...] The serialization is done through YAML.

So the column should contain a YAMLized version of your bulk_action but '

 "{\"location\"=>{\"commands\"=>{\"custom_command_one\"=>\"true\", \"custom_command_two\"=>\"true\"}}}"

Is not a YAML hash. If you want to muck around with the raw serialized data then you should use something like

 "{\"location\"=>{\"commands\"=>{\"custom_command_one\"=>\"true\", \"custom_command_two\"=>\"true\"}}}".to_yaml

Upvotes: 0

Dzmitry
Dzmitry

Reputation: 466

I've not found correct answer too (YAML.dump, to_yaml - the same error in Rails 4.0.1). Only eval helps me. It's not a big security problem because I use it in admin pane.

  params[:branch][:features_attributes][:primary] = eval params[:branch][:features_attributes][:primary]
  params[:branch][:features_attributes][:secondary] = eval params[:branch][:features_attributes][:secondary]
  if @branch.update_attributes(params[:branch]) ...        

Upvotes: 0

moritz
moritz

Reputation: 25757

Can you switch to JSON within the textarea so that parsing it isn't so dangerous. Because what you would have to do is eval the respective params entry in a controller or the model which enables users to do whatever they want with the user under which your app is running. With JSON, you could just use JSON.parse before setting the model attribute.

Upvotes: 1

Related Questions