I'm filling out the sign up form on my site. I type in first name, last name, email, password, and password confirmation. When I click "submit", my form clears, and the console tells me the user already exists (even though it doesnt, and throws me a success message). In short: I'm trying to create a new user, and save it to my database.
See below:
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE
"users"."firstname" IS NULL LIMIT 1 (0.1ms) rollback transaction
Rendered users/new.html.erb within layouts/application (2.4ms)
Completed 200 OK in 136ms (Views: 68.4ms | ActiveRecord: 0.9ms)
See code below - cheers!
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "You signed up successfully"
flash[:color] = "valid"
redirect_to #user
else
flash[:notice] = "Form is invalid"
flash[:color] = "invalid"
render "new"
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
def show
#user = User.find(params[:id])
end
def new
end
end
user.rb
class User < ActiveRecord::Base
validates_length_of :password, :in => 6..20, :on => :create
validates :password_confirmation, presence: true, if: -> { new_record? || changes["password"] }
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<%= render 'shared/error_messages' %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
As you can see from the generated SQL, firstname is nil on your new user.
That is because this line contains typos:
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation
should be:
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation
I think you are missing some params. Using devise? Try to create a new user via rails console to see what parameters are needed.
Check the strong parameters given as first_name and last_name but in form it is firstname and lastname.
Related
I'm using nested attributes to user adresses.
class User < ApplicationRecord
accepts_nested_attributes_for :address, update_only: :true
I'm passing the address id as hidden field at sign-in.
<%= form_for (#user), :html => {:class => 'lead-form'} do |f| %>
<%= f.email_field :email, class:'form-control', placeholder: "Email", required: true %>
<%= f.password_field :password, class:'form-control', placeholder: "Senha", required: true %>
<%= f.fields_for :address do |addresses_form| %>
<%= addresses_form.hidden_field :address_id, :value => #address.id %>
<% end %>
<%= f.submit 'Pedir meu convite', class: "button lead-form-button" %>
<% end %>
The e-mail is validate as uniqueness. So, when somebody try to sign in with an already taken e-mail the address params are passed as nil and i get the error:
NoMethodError in Users#create
undefined method `id' for nil:NilClass
Any idea how can i fix that?
Here is my controller:
def new
#user = User.new
#address = #user.build_address
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Você está dentro. Esse é seu perfil! =) "
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:id, :primeiro_nome, :sobrenome, :email, :cpf, :telefone, :sexo, :data_de_nascimento, :password, address_attributes: [:id, :logradouro, :bairro, :cidade, :uf, :complemento, :cep])
end
ps: The association is one to one. How can i call the address attributes if i don't pass as hidden.
Address belongs to user? If yes, you just call #user.address
Maybe you should try using try method:
<%= addresses_form.hidden_field :address_id, :value => #address.try(:id) %>
Working through the railstutorial.org . Currently on the Update Profile page part of it. When leaving the Password and Password Confirmation fields empty, only the Password is too short error comes up, though in the tutorial screenshot Password confirmation can't be blank message is present. But, it does show up when the Password field is filled and Password Confirmation field is left empty.
edit.html.erb :
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
<%= gravatar_for #user %>
change
</div>
</div>
users_controller.rb:
.
.
.
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
.
.
.
user.rb:
class User < ActiveRecord::Base
has_secure_password
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
before_create :create_remember_token
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
Your User.rb model is out of sync with the one MHartl uses at that point in the tutorial. Specifically, notice that he has an explicit validates :password_confirmation, presence: true in addition to the validates :password, length: { minimum: 6 }. When you call #user.update_attributes, it hits these validators and, in his case, both fail, whereas in your User.rb model there is not the presence validator.
When you have the password field filled in, you're hitting validators defined in has_secure_password rather than in your model, which is why they appear then.
Make sure you include the password and password_confirmation in your allowed parameters in you controller:
private
def user_params
params.required(:user).permit(:name, :email, password,:password_confirmation)
end
EDIT:
As the other had already pointed out you miss the validation of password_confirmation
validates :password_confirmation, presence: true
I have a user model: :name, :email, :password
Here is my form:
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.text_field :password %>
<%= f.submit "Save" %>
<% end %>
I want to display the form without :password, when I remove the password attribute and submit the form, the fields do not update in the database. I get a rollback.
You can do something like:
object.attribute = value
object.save(:validate => false)
This will never call your validation
But remember this will never validates your email and name fields as well if they are blank.
Commenting this line in my user model fixed the issue:
validates :password, length: { minimum: 6 }
However, as noted in the comments, it is not sufficient.
A user can edit their own profile, an admin can edit their own and others profiles.
Here is a solution:
def update
#user = User.find_by_id(params[:id])
if current_user?(#user)
if #user.update_attributes(user_params)
flash[:success] = "Your profile updated"
redirect_to #user
else
render 'edit'
end
else
if #user.update_attributes(admin_params)
flash[:success] = "Others profile updated"
redirect_to #user
else
render 'admin_edit_user'
end
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
def admin_params
params.require(:user).permit(:name, :email, :password) if current_user.admin?
end
If you want to skip validations on a particular form for a particular attribute, you could do the following.
Class User
attr_accessor :skip_password_validation
validate_presence_of :password, unless: -> { skip_password_validation }
end
This creates a virtual attribute that you can set and read but won't get persisted in the database.
Then you can put a hidden field for :skip_password_validation in whatever form you want and it will skip the validations.
<%= f.hidden_field :skip_password_validation, value: true %>
You'll also have to add this param to the user_params in the controller.
I set the Devise to allow an email only registration process that sends an email with a link to confirm account by setting password.
I would like to set the firstname and lastname at the same time than password setup (and account confirmation) but I don't know how to do.
Here is my confirmation controller (working), Crafter being my standard User.
I tried Crafter.update_attributes(params[:crafter]) with no luck.
Any help would be wonderful. TY.
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:crafter])
if #confirmable.valid?
#confirmable.update_attributes(params[:crafter])
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_allready_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
And my "confirm account" form :
<%= form_for resource, :as => resource_name, :url => update_user_confirmation_path, :html => {:method => 'patch', class: "form-horizontal", role: "form"}, :id => 'activation-form' do |f| %>
<%= f.text_field :firstname, autofocus: true, placeholder: "Firstname", class: "form-control" %>
<%= f.text_field :lastname, placeholder: "Lastname", class: "form-control" %>
<% if #requires_password %>
<%= f.password_field :password, autofocus: true, placeholder: "Choose A Password", class: "form-control" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm Password", class: "form-control" %>
<% end %>
<%= hidden_field_tag :confirmation_token,#confirmation_token %>
It's hard to tell but I'd guess you're not getting the firstname, lastname params passed to the controller.
Rails 4 uses something called strong parameters; the README there has lots of good info if you want to go beyond my example below.
In your ApplicationController you need to allow for additional params, add these lines:
app/controllers/application_controller.rb
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:firstname, :lastname, :email, :current_password, :password, :password_confirmation)
end
end
That should fix things.
In your CrafterController you could do something like this:
#confirmable.update_with_password(account_update_params)
Just do be a bit cleaner
I'm not sure how to validate the confirm password element, this code seems to work, but the validates confirmation of password doesn't seem to compare the passwords as expected, just need a hand getting used to Rails 4's new ways.
Sign Up View:
Sign Up
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :email, class: "form-control", autofocus: "", required: "", placeholder: "Email address" %>
<%= f.password_field :password, class: "form-control", required: "", placeholder: "Password" %>
<%= f.password_field :password_confirmation, class: "form-control", required: "", placeholder: "Confirm Password" %><br/>
<p class="button"><%= submit_tag "Register", class: "btn btn-lg btn-primary btn-block" %></p>
<% end %>
</div>
Users Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :passord_confirmation, :salt, :encrypted_password)
end
end
Users Model:
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_presence_of :password, on: :create
validates :password, confirmation: :true
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
private
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
It looks like your model's password confirmation may be off, unless you have your own private method confirmation
Try changing your model from validates :password, confirmation: :true to validates_confirmation_of :password
The API has some examples as well.
You need to make the password and password_confirmation accessible. For example:
attr_accessor :password
attr_accessible :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true
Try adding that, and see if it makes a difference.