Skrypter Skrypter - 5 months ago 46
Ruby Question

How to update multiple Models in single action? (Rails 4)

I have 4 models and I have a very important reason to update all models (maybe in separate actions but in 1 action, if possible).

If I want to update 1 model I can use

def update
@modelA = ModelA.find(params[:id]
respond_to do |format|
if @modelA.update(modelA_params)
format.html { redirect_to :back, notice: "" }
format.json { head :no_content }
else
format.json { render json: @modelA.errors, status: :unprocessable_entity }
format.html { render :show }
end
end
end


with appropriate params. How would I change this if I have Configuration page and I want to update 4 models (separately of course)? I have 4 columns for each model and I want to update each column the appropriate attribute from model. In my case, I am using the x-editable gem to update attributes.


The question is...there is any hack for that? like batch update with
case condition or should i create update action for every model to
update them?(in configuration_controller)

Answer

If you can pass the parameters for all 4 models to the action, you could do something like:

# app/controllers/configuration_controller.rb
def update
  @a = ModelA.find(params[:model_a][:id])
  @b = ModelB.find(params[:model_b][:id])
  @c = ModelC.find(params[:model_c][:id])
  @d = ModelD.find(params[:model_d][:id])

  @a.assign_attributes(model_a_params)
  @b.assign_attributes(model_b_params)
  @c.assign_attributes(model_c_params)
  @d.assign_attributes(model_d_params)

  respond_to do |format|
    if @a.save && @b.save && @c.save && @d.save
      format.html { redirect_to :back, notice: "" }
      format.json { head :no_content }
    else
      format.json { render json: [@a.errors, @b.errors, @c.errors, @d.errors], status: :unprocessable_entity }
      format.html { render :show }
    end
  end

  private

  def model_a_params
  params.require(:model_a).permit(:id) # and other permitted params
end

#... same for the other 3 models

I have used assign_attributes here, as you may want atomicity in the action.

[EDIT]

If, on the other hand, you want to update the models in separate actions, then simply have 4 separate actions in your controller:

# app/controllers/configuration_controller.rb
def update_model_a
  @a = ModelA.find(params[:id])
  respond_to do |format|
    if @a.save
      format.html { redirect_to :back, notice: "" }
      format.json { head :no_content }
    else
      format.json { render json: [@a.errors, @b.errors, @c.errors, @d.errors], status: :unprocessable_entity }
      format.html { render :show }
    end
  end

  def update_model_b
    @b = ModelB.find(params[:id])
    respond_to do |format|
      if @b.save
        format.html { redirect_to :back, notice: "" }
        format.json { head :no_content }
      else
        format.json { render json: [@a.errors, @b.errors, @c.errors, @d.errors], status: :unprocessable_entity }
        format.html { render :show }
      end
    end

# ... etc.

private

def model_a_params
  params.require(:model_a).permit(:id) # and other permitted params
end

def model_b_params
  params.require(:model_b).permit(:id) # and other permitted params
end

# ... etc.

#... same for the other 3 models