LightBox
LightBox

Reputation: 3425

How to get data in params hash?

I have a Car model and an Brand model (each of which has_and_belongs_to_many). I am trying to create a new brand for a car. The car model accepts nested attributes for brand.

When I try

@car = Car.find(params[:car_id])
@brand = @car.brands.build(params[:brand])

I get a mass assignment error:

Can't mass-assign protected attributes: brands

Brands Controller:

class BrandsController < ApplicationController
 def create
  debugger
  @car = Car.find(params[:car_id])
  @brand= @car.brands.build(params[:brand])
  if @brand.save
   redirect_to @car
  else
   #do something
  end
 end 
end

Calling the debugger with

pp params

Shows:

"brand"=>{"brands"=>{"name"=>"BMW"}},
"commit"=>"Create Brand",
"action"=>"create",
"controller"=>"brands",
"car_id"=>"4"}

The view looks like this:

<%= form_for([@car, @car.brands.build]) do |f| %>
    <div class="field">
        <%= f.fields_for :brands do |a| %>
            <%= a.text_field :name, placeholder: "New Brand" %>
        <% end %>
    </div>
    <p><%= f.submit %></p>
<% end %>

I want the application to be secure so I want to set the brand attributes individually as opposed to passing the whole "brand" hash (which incidentally does not work). How can I get the brand name? Something like

params[:brands].name?

Edit:

I can get the brand name with 'params[:brand][:brands][:name]'. Now it turns out that the brands table does not have a car_id since it is connected to a car with a join table.

Upvotes: 0

Views: 341

Answers (2)

Cluster
Cluster

Reputation: 5617

Although having a model accept attributes for another is a useful tool in some cases, it often tends to be cumbersome and violates single responsibility principal not only at the model level, but also at the controller and view layer. Ultimately making it more difficult to refactor should your design need to change.

Keeping with the philosophy of REST, instead of creating Brands through Cars, use a BrandsController to handle the lifecycle of a Brand.

As for your requirement of security, setting a model's attributes manually does not provide any more security then passing the whole params hash assuming you are properly using attr_accessible. Giving it a whitelist of acceptable paramaters will tell the AR interface that everything else should be denied.

class Brand < ActiveRecord::Base
  attr_accessible :name
end

So if a brand belongs to a car, and someone tried to change the relationship by hacking a car_id into the form it would reject car_id from the hash, possibly even raising an exception depending on your settings.

config.active_record.mass_assignment_sanitizer = :strict

That will cause a ActiveModel::MassAssignmentSecurity::Error instead of just the Warning that is output into the logs.

Upvotes: 1

gabrielhilal
gabrielhilal

Reputation: 10769

<%= form_for @car do |f| %>
  <div class="field">
    <%= f.fields_for :brands do |a| %>
        <%= a.text_field :name, placeholder: "New Brand" %>
    <% end %>
  </div>
<p><%= f.submit %></p>
<% end %>

In your model:

class Car < ActiveRecord::Base
  has_many :brands
  accepts_nested_attributes_for :brands
  attr_accessible :brand_attributes
end

Upvotes: 1

Related Questions