Can't mass-assign protected attributes issue using devise signup form - ruby-on-rails-3.1

I am using rails 3.1 and ruby 1.9.3 for my application.
And the issue is, I am getting "Can't mass-assign protected attributes" while saving the details.
I have User model as:
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
has_one :profile
accepts_nested_attributes_for :profile
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_attributes, :first_name, :last_name
end
And Profile model as:
class Profile < ActiveRecord::Base
belongs_to :user
end
I am having few fields in "User" model and personal data like first_name, last_name and etc in "Profile" model. And I am trying to get all the required data by customizing the devise signup form as follows:
<%= 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 %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<%= f.fields_for :profile do |builder| %>
<div><%= builder.label :first_name, "First Name" %><br />
<%= builder.text_field :first_name %></div>
<div><%= builder.label :last_name, "Last Name" %><br />
<%= builder.text_field :last_name %></div>
<% end %>
<div><%= f.submit "Sign up" %></div>
Can anyone please tell me where I am going wrong.

If the first_name and last_name fields are of the profile model then you should mention it as attribute_accessible in the profile model itself.
your profile model should look like
class Profile < ActiveRecord::Base
belongs_to :user
attribute_accessible :first_name, :last_name
end

Related

How to set an optional reset_password_keys for Devise

In a Rails app, the config Devise file has a reset_password_keys option. Is there a way to make one of the keys optional?
Currently I have this setting config.reset_password_keys = [ :email, :account_id ].
I would like to make the :account_id an optional key if there is no account_id present.
The following worked for me:
#Admin.rb
devise :database_authenticatable, :encryptable,
:recoverable, :rememberable, :trackable, :reset_password_keys => [:email]
There's an entry on Devise's wiki that deals with that.
I'd suggest you do the following:
# on config/initializers/devise.rb:
config.reset_password_keys = [ :email, :account_id ]
# on User model
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
where(account_id: conditions[:account_id], email: conditions[:email]).first
end
# on passwords/new.html.erb
<%= f.label :email %><br />
<%= f.text_field :email %></p>
<%= f.label :account_id %><br />
<%= f.text_field :account_id %></p>

validations failing as blank when fields not blank

I am runnning ruby 1.9.3 and rails 4.1.4
trying the following validations
in the models\profile.rb
attr_accessor :password
validates :name, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :password, :confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
before_save :encrypt_password
in view profiles\new.html.erb
from the form
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.label :interests %><br />
<%= f.text_area :interests %>
</p>
<p>
<%= f.label :zip %><br />
<%= f.text_field :zip %>
</p>
<p>
<%= f.label :country %><br />
<%= f.country_select :country, ["United Kingdom"] %> </p>
using strong parameters controllers\profile_controller,rb
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def create
#profile = Profile.new(params[profile_params])
if #profile.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
private
## Strong Parameters
def profile_params
params.require(:profile).permit(:name, :email, :password, :interests,:zip)
end
end
validations always fail saying fields are blank. By causing an exception the report shows model profile with its fields populated. It is as if the data is simply not accessible so I suspect I'm misuing strong parameters sonehow.
Any opinions welcomed.
The profile_params function is going to return a hash for you with the data directly.
What that basically does is it filters the params hash for the 'profile' key's value for the keys named as the parameters of permit(...)
So the way to fix is to write
#profile = Profile.new(profile_params)
instead of
#profile = Profile.new(params[profile_params])
With strong parameters you almost never interact with the params array directly from controller actions (such as create). The private strong parameter methods will handle that for you and by that, only safe, filtered data will be given to your controller actions.

Not saving on the database from nested form

I want to create a dummy account for a new User object. Whenever a user signs up in my app it should automatically create an account for him.
But what's happening is, I'm saving in the database the new User, but not the new Account. I'm also not aware what's the best way to respect the MVC pattern design when it comes to this situation. I'm afraid of replicating code or to have one controller doing the work of two.
The sign up form, has nested resources (and it's build on top of devise)
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :name %><br />
<%= f.text_field :name %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<p>Account name</p>
<%= f.fields_for :account do |builder| %>
<fieldset>
<%= builder.label :title %>
<%= builder.text_field :title %>
</fieldset>
<% end %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
My User Model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :accounts
accepts_nested_attributes_for :accounts
end
and my Account Model:
class Account < ActiveRecord::Base
belongs_to :user
end
My User Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
##flash[:success] = "Welcome to the HighTide!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, accounts_attributes: [:id, :title])
end
end
and finally the Account Controller:
class AccountsController < ApplicationController
def new
#account = #user.accounts.new
end
def create
#account = current_user.accounts.new(account_params)
if #account.save
redirect_to '/'
end
end
private
def account_params
params.require(:account).permit(:title, :user_id, products_attributes: [:id, :title, :units], bookings_attributes: [:id, :name, :check_in, :check_out])
end
end
EDIT:
routes.rb file
Hightide::Application.routes.draw do
devise_for :users, path_names: {sign_in: "login", sign_out: "logout"}
resources :users do
resources :accounts
end
resources :sessions
match '/users/:user/edit', to: 'users#edit', via: [:post, :get]
devise_scope :user do
root to: 'static_pages#home'
match '/sessions/user', to: 'devise/sessions#create', via: :post
end
Your form_for can only send its data to a single controller method, and in this case it is sending the params from your form to the Devise registrations controller. Those params include the values for the new account, but they never reach your users#create or accounts#create methods.
You'll probably have to create a custom Devise controller. Take a look at this SO question and answer, which lays it out nicely. Nested registration data in Rails 3.1 with Devise.

Rails: Undefined method create for nil class

I am creating a sample project but when I am trying to create a new post getting an error "undefined method create for nil class"
My code is as follows.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :post, dependent: :destroy
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
posts_controller.rb
class PostsController < ApplicationController
def create
#user = current_user
if #user.post.blank?
#post = #user.post.create(params[:post].permit(:title, :text))
end
redirect_to user_root_path
end
end
new.html.erb
<%= form_for([current_user, current_user.build_post]) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
But after trying so many times i have made some changes and it started working but i dont know what is the difference between both the codes.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts, dependent: :destroy
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
posts_controller.rb
class PostsController < ApplicationController
def create
#user = current_user
if #user.posts.blank?
#post = #user.posts.create(params[:post].permit(:title, :text))
end
redirect_to user_root_path
end
end
new.html.erb
<%= form_for([current_user, current_user.posts.build]) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
my routes.rb is
UserBlog::Application.routes.draw do
devise_for :users, controllers: { registrations: "registrations" }
resources :users do
resources :posts
end
# You can have the root of your site routed with "root"
root 'home#index'
end
Please help me and tell me what is the difference between both the codes ?
The difference is in the helper methods that are added allowing you to build or create a new association object. The approach is slightly different for a has_one compared to a has_many association.
For a has_one association the method to create a new associated object would be user.create_post.
For a has_many association the method to create a new associated object would be user.posts.create.

Devise Authentication with Accounts and Users

I am working on implementing a top-level account that has many users.
I am working off the solution for this question: Use both Account and User tables with Devise
What works:
After user submits registration form (below) the user and account is created as desired.
What doesn't work:
The user is not being authenticated before the redirect_to accounts_path. In order to authenticate, the user must click login and enter the credentials they just signed up with. Instead I need the user to be authenticated before the redirect.
I've been working on this for a few hours, tried a few approaches, but nothing seems to work.
Could someone help me with the code to authenticate the user after the user/account is successfully created. THANKS!
Models
class Account < ActiveRecord::Base
has_many :users, :inverse_of => :account, :dependent => :destroy
accepts_nested_attributes_for :users
attr_accessible :name, :users_attributes
end
class User < ActiveRecord::Base
belongs_to :account, :inverse_of => :users
validates :account, :presence => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
Routes
resources :accounts, :only => [:index, :new, :create, :destroy]
controllers/accounts_controller.rb
Controllers
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build # build a blank user or the child form won't display
end
def create
#account = Account.new(params[:account])
if #account.save
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
end
views/accounts/new.html.erb view
<h2>Create Account</h2>
<%= form_for(#account) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :users do |user_form| %>
<div class="field"><%= user_form.label :email %><br />
<%= user_form.email_field :email %></div>
<div class="field"><%= user_form.label :password %><br />
<%= user_form.password_field :password %></div>
<div class="field"><%= user_form.label :password_confirmation %><br />
<%= user_form.password_field :password_confirmation %></div>
<% end %>
<div class="actions">
<%= f.submit "Create account" %>
</div>
<% end %>
You need to manually sign the user in. In the accounts controller you need a sign_in(user), where user is the actual User model record that you want to sign in.
The problem with that is that you have a one account to many users relationship. Therefore, you'll need to get access to the single user somehow, e.g:
def create
#account = Account.new(params[:account])
if #account.save
sign_in(#account.users.first)
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
You need to add
before_filter :authenticate_user!, :except => [:new,:create]
to accounts controller in order to provide authentication to rest of the actions

Resources