Search code examples
ruby-on-railsruby-on-rails-3devisenested-attributesmass-assignment

Can't mass-assign protected attributes for creating a has_many nested model with Devise


I've watched the RailsCast, another nested attributes video, lots of SO posts, and fought with this for a while, but I still can't figure it out. I hope it's something tiny.

I have two models, User (created by Devise), and Locker (aka, a product wishlist), and I'm trying to create a Locker for a User when they sign up. My login form has a field for the name of their new Locker (aptly called :name) that I'm trying to assign to the locker that gets created upon new user registration. All I'm ever greeted with is:

WARNING: Can't mass-assign protected attributes: locker

I've tried every combination of accepts_nested_attributes and attr_accesible in both of my models, yet still nothing works. I can see from the logs that it's being processed by the Devise#create method, and I know Devise isn't smart enough to create my models how I want :)

Here's the relevant bits of my two models:

# user.rb    
class User < ActiveRecord::Base
  attr_accessible :username, :email, :password, :password_confirmation, :remember_me, :locker_attributes

  # Associations
  has_many :lockers
  has_many :lockups, :through => :lockers

  # Model nesting access
  accepts_nested_attributes_for :lockers
end

and

# locker.rb
class Locker < ActiveRecord::Base
  belongs_to :user
  has_many :lockups
  has_many :products, :through => :lockups 

  attr_accessible :name, :description
end

# lockers_controller.rb (create)
    @locker = current_user.lockers.build(params[:locker])
    @locker.save

I'm assuming I need to override Devise's create method to somehow get this to work, but I'm quite new to rails and am getting used to the black box "magic" nature of it all.

If anyone can help me out, I'd be incredibly thankful. Already spent too much time on this as it is :)

EDIT: I realized I omitted something in my problem. My Locker model has three attributes - name, description (not mandatory), and user_id to link it back to the User. My signup form only requires the name, so I'm not looping through all the attributes in my nested form. Could that have something to do with my issue too?

EDIT 2: I also figured out how to override Devise's RegistrationsController#create method, I just don't know what to put there. Devise's whole resource thing doesn't make sense to me, and browsing their source code for the RegistrationsController didn't help me much either.

And for bonus points: When a user submits the login form with invalid data, the Locker field always comes back blank, while the regular Devise fields, username & email, are filled in. Could this also be fixed easily? If so, how?


Solution

  • Someone helped me figure out the solution in a more "Rails 4'y" way with strong attributes & how to override Devise's sign_up_params (to catch all the data coming from my signup form).

      def sign_up_params
        params.require(:user).permit(:username, :email, :password, :lockers_attributes)
     end
    

    Gemfile addition: gem 'strong_parameters'

    Commenting out the attr_accessible statement in my user.rb file, since apparently strong parameters eliminate the need for attr_accessible declarations.

     # attr_accessible :username, :email, :password, :password_confirmation, :lockers
    

    And the/a correct way of building a Locker before submitting the form: at the beginning of the nested form:

    <%= l.input :name, :required => true, label: "Locker name", :placeholder => "Name your first locker" %>

    Thanks again for all your help. I know a question like this is difficult to answer without seeing the whole codebase.