Rails render json including methods with parameter

2020-07-11 05:34发布

问题:

I have this render in a method controller:

render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :methods => :translatedCity}}}}

but translatedCity method in the model should accept language parameter.

  def translatedCity     
    language = params[:language]
    puts "translatedCity.language=" + language  
    city = TranslationService.translateCity(self.city_id, language)        
    return city.element_translation
  end

I have the parameter in the controller. Is there any way to pass this parameter to the method in the model?

回答1:

Unfortunately the process of passing methods that require params to_json is not nicely built into rails. a typical pattern is to modify the as_json method on the model to accept custom keys, and then calling to_json (or render :json)

# city.rb model
def translatedCity(language)
  puts "translatedCity.language=" + language  
  city = TranslationService.translateCity(self.city_id, language)        
  return city.element_translation
end

def as_json(options = {})
  json_to_return = super
  if options.has_key? :translatedCity
    city_translation = self.translatedCity(options[:translatedCity][:language])
    json_to_return[:translatedCity] = city_translation
  end

  return json_to_return
end

now in your controller you pass everything in accoringdly

render :json => @boats, 
  :include => {
    :port => {
      :only => [:id, :name], 
      :include => {
        :city => {
          :only => [:id, :name], 
          :translatedCity => params[:language]
        }
      }
    }
  }


回答2:

All answers are good!

I just did something like:

In controller:

boat.port.language = params[:language]

In model:

attr_accessor :language
  @language  

  def translatedCity            
    city = TranslationService.translateCity(self.city_id, language)        
    return city.element_translation
  end

What do you think?



回答3:

as_json doesn't support methods that require arguments, but this is a good place to use a Presenter (or Decorator, or View Model, depending who you talk to) that builds the JSON you want. Draper is a gem that helps with that if you want one, and your controller would look something like

data = @boats.map do |boat|
  BoatPresenter.new(boat).as_json(language: params[:language])
end
render json: data

Then BoatPresenter would add the extra data you want in its as_json:

def as_json(options = {})
  @boat.as_json(options).tap do |json|
    json[:translated_city] = @boat.translated_city(options[:language])
  end
end

You can also have BoatPresenter add your default includes, etc. if you want a standard set without the controller having to worry about it. And if you don't want to do the array mapping yourself, Draper also offers collection presenters (eg BoatsPresenter).


If you're averse to adding a class, you could also override as_json directly on Boat to handle a language option:

def as_json(options = {})
  super(options).tap do |json|
    json[:translated_city] = translated_city(options[:language])
  end
end


回答4:

You can set 'language' in a session variable and set it before executing to_json in your controller, and destroy session in your method before return as follwoing

def translatedCity(language=nil)
    language ||= session[:language]
    puts "translatedCity.language=" + language  
    city = TranslationService.translateCity(self.city_id, language)        
    session[:language] = nil #destroy session, or use session.delete(:language)         
    return city.element_translation
end


session[:language] = language
render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :methods => :translatedCity}}}}