0

So, I'm trying to add a selection for a belongs_to relationship in the User Registration form.

For example:

Here's the User model:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me
  # attr_accessible :title, :body
  belongs_to :thing
  validates_presence_of :thing
end

Thing model:

class Thing < ActiveRecord::Base
  attr_accessible :name
  has_many :user
  validates_presence_of :name
  validates :name, :uniqueness => { :case_sensitive => false }
end

So, I've added some code to the app/views/devise/registration/new.html.haml file:

%h2 Sign up

= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
  = devise_error_messages!

  %div
    = f.label :email
    %br
    = f.email_field :email, :autofocus => true

  %div
    = f.label :password
    %br
    = f.password_field :password

  %div
    = f.label :password_confirmation
    %br
    = f.password_field :password_confirmation

  %div
    = f.label :thing
    %br
    = f.select :thing, @things.map{ |r| [r.name, r.id] }

  %div
    = f.submit "Sign up"

= render "devise/shared/links"

So this all works fine, I can select the things from the select box. However, handling the submit is what I'm getting confused about. With this how it is, I'm getting a "can't mass assign protected attributes" error, which is what it should be doing.

How can I override the Devise controller to handle this? I've tried something like:

class RegistrationsController < Devise::RegistrationsController
  def new
    @things = Thing.all.sort_by{|e| e[:name]}
    super
  end
  def create
    @user = User.new(params[:user][:email], params[:user][:password])
    @user.thing = params[:user][:thing]
    super
  end
end 

but I get the feeling this is not anywhere close to what I'm supposed to do. Any help would be appreciated! Thanks!

justindao
  • 2,273
  • 4
  • 18
  • 34

2 Answers2

0

Adding a relationship between two models can't be done without database changes. Rails don't change the database automatically to you, you need to write a migration for that. You can check on rails guides how to add a rails has_many relationship, and overall, I recommend take a good read in the model/view/controller sections form index before start using rails. It should take you no more than two days to read this stuff and if you understand what you are doing you will be able to do things better/faster.

fotanus
  • 19,618
  • 13
  • 77
  • 111
  • I think I might have been a little confusing - The relationship between Thing and User isn't the problem - I'm trying to assign a User's Thing when they register using Devise. However, this can't be done because I can't mass assign protected attributes through `form_for`. I'm seeing if anyone knows a way to do this just modifying the standard Devise layout a bit. – justindao May 28 '13 at 17:47
  • @h7u9i: So I suppose your question is how to [create a form for with nested resources](http://stackoverflow.com/questions/2034700/form-for-with-nested-resources) instead? – fotanus May 28 '13 at 18:42
  • Not exactly. It's mostly dealing with Devise, and how exactly to override/rewrite the create method for registration. – justindao May 28 '13 at 18:50
  • @h7u9i And have you tried to add accepts_nested_attributes_for :thing? – fotanus May 28 '13 at 19:01
0

So I figured it out.

I had to add a create method in the Registrations controller to override the Devise one.

class RegistrationsController < Devise::RegistrationsController
  def new
    @things = Thing.all.sort_by{|e| e[:name]}
    super
  end

  def create
    @things = Thing.all.sort_by{|e| e[:name]}
    @user = User.new(email: params[:user][:email], password: params[:user][:password], password_confirmation: params[:user][:password_confirmation])
    @user.thing = Thing.find(params[:user][:thing])

    if @user.save
      if @user.active_for_authentication?
        set_flash_message :notice, :signed_up if is_navigational_format?
        sign_up(:user, @user)
        respond_with @user, :location => after_sign_up_path_for(@user)
      else
        set_flash_message :notice, :"signed_up_but_#{@user.inactive_message}" if is_navigational_format?
        expire_session_data_after_sign_in!
        respond_with @user, :location => after_inactive_sign_up_path_for(@user)
      end
    else
      clean_up_passwords @user
      respond_with @user
    end
  end
end 

I basically copy and pasted the one from the Devise source code and put the save code for my object in there.

justindao
  • 2,273
  • 4
  • 18
  • 34