Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual attribute not moved to the model hash inside params

I'm having a problem in my Rails 3.2 app where a virtual attribute sent restfully via JSON is not in the right place in the params hash. Well, it isn't where I expect. It remains to be seen if my expectations are correct. :)

I have a model using the standard virtual attribute pattern, like this:

class Track < ActiveRecord::Base
  def rating
    # get logic removed for brevity
  end

  def rating=(value)
    # set logic
  end

  def as_json(options={}) # so my method is in the JSON when I use respond_with/to_json
    super(options.merge(methods: [:rating]))
  end
end

The JSON sent to my controller looks like this:

{"id":1,"name":"Icarus - Main Theme 2","rating":2}

To be clear, name and id are not virtual, rating is.

I end up with this in the params hash, after rails does its magic:

{"id"=>"1", "name"=>"Icarus - Main Theme 2", "rating"=>2, "track"=>{"id"=>"1", "name"=>"Icarus - Main Theme 2"}}

As you can see, id and name make it to the nested :track hash, but rating does not. Is this expected behavior? It breaks the (somewhat) standard practice of using the nested hash in the controller because the nested hash does not contain all the parameters I need.

Track.update(params[:id], params[:track]) # :track is missing rating

Thanks for your help!

like image 401
Tetious Avatar asked Feb 19 '12 22:02

Tetious


1 Answers

I recently ran into this gotcha as well. The problem is, the params wrapper is looking at your model Track.attribute_names to determine how to map the data into a :track => {params} hash. If you don't have a model associated, the default will be to wrap the params based on the controller name, and include all of the values:

class SinglesController < ApplicationController
  def create
    #params[:single] will contain all of your attributes as it doesn't 
    # have an activerecord model to look at.
    @track_single = Track.new(params[:single]) 
  end
end

You can call wrap_parameters in your controller to tell action controller what attributes to include when its wrapping your params, like so:

class TracksController < ApplicationController
  wrap_parameters :track, :include => :rating
  #other controller stuff below
end

See more here: http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html

like image 182
sharpone Avatar answered Jan 03 '23 10:01

sharpone