Virtual attribute does not move to model hash inside parameters

I am having a problem in my Rails 3.2 application where the virtual attribute sent using JSON is not in the right place in the params hash. Well, this is not the place where I expect. It remains to be seen whether my expectations are true. :)

I have a model using a standard virtual attribute template, for example:

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 is as follows:

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

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

I end up with this in the hash parameters after the rails do their 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 go to the hash file of the attached track: but there is no rating. Is this expected behavior? It violates the (somewhat) standard practice of using a 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!

+6
source share
3 answers

I recently ran into this error. The problem is that the params shell looks at your Track.attribute_names model to determine how to map data to a hash: track => {params}. If you do not have a related model, by default it will wrap the parameters based on the name of the controller and include all 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 the action controller which attributes to include when it wraps your parameters, for example:

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

More details here: http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html

+7
source

Perhaps if you enter the virtual attribute rating inside the nested hash, for example:

 def as_json(options={}) super(options.merge(:track => {:methods => @rating})) end 

He will behave as you expected.

0
source

I just stumbled upon this problem and realized a pretty decent solution. Add the following to your ApplicationController:

 wrap_parameters exclude: [:controller, :action, :format] + ActionController::ParamsWrapper::EXCLUDE_PARAMETERS 

Thus, everything is nested under your resource (except that Rails adds a params hash), and you no longer have to add a controller to the wrap_parameters call .: D

0
source

Source: https://habr.com/ru/post/908856/


All Articles