I'm going through the well known Rails tutorial and I'm hitting something I don't understand.
If I try going against my validation routines by entering a user whose name or password is too short, when I try to do user.save in the rails console, the error I get is User Exists.
Why am I getting that particular error, which is of course not the right one? I do have a uniqueness validation set up, but that's not the one that should be triggered here.
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50, minimum: 8 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 8 }
end
Is it possible that you added that user into the database before the validation was put into place? I often have to clean out my test database when I change validation code.
Related
I have a user model with a username and email. Currently, the email is used to log in, however I want users to be able to log in with their username as well. In order to do this, I have to ensure that one users username is not the same as another's email, and vice versa.
My uniqueness validations for each individual field work fine, but I can't get validation across the columns to work.
My validations are as follows:
validates(:username, presence: true, length: { maximum: 25 }, uniqueness: {case_sensitive: false })
validates(:username, uniqueness: {scope: :email, case_sensitive: false});
And
validates(:email, presence: true, length: { maximum: 255 },
format: {with: VALIDATE_EMAIL_REGEX}, uniqueness: { case_sensitive: false })
validates(:email, uniqueness: {scope: :username, case_sensitive: false})
How can I validate the uniqueness of these two columns?
Any help is appreciated.
How can I validate the uniqueness of these two columns?
You could write a custom validation method:
validate :uniq_username_and_email
private
def uniq_username_and_email
self.errors.add(:username, 'already taken') if User.exists?(email: username)
self.errors.add(:email, 'already taken') if User.exists?(username: email)
end
Assuming that your model is named User
In Michael Hartl's tutorial, the following is stated.
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
In case you’re worried that Listing 9.10 might allow new users to sign up with empty passwords, recall from Section 6.3.3 that has_secure_password includes a separate presence validation that specifically catches nil passwords.
My question is, how is the has_secure_password validation working if it allows the test to pass? I do not understand, clearly the has_secure_password validation is not "catching" this rule to bypass an empty password.
Further, how does rails know not to set and save the empty passwords to the user? please help me.
You can check the documentation here , it explains everything in details,
According to the source code:
def has_secure_password
.....
if options.fetch(:validations, true)
include ActiveModel::Validations
# This ensures the model has a password by checking whether the password_digest
# is present, so that this works with both new and existing records. However,
# when there is an error, the message is added to the password attribute instead
# so that the error message will make sense to the end-user.
validate do |record|
record.errors.add(:password, :blank) unless record.password_digest.present?
end
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
validates_confirmation_of :password, allow_blank: true
end
..........
end
if you did not call has_secure_password(validations: false) the three types of validations will be added. i think the reason of the passing test is :
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
The :allow_nil option skips the validation when the value being
validated is nil.
To Add
how does rails know not to set and save the empty passwords to the
user?
I think it's because the params[:user][:password] is blank and not nil
My code require a user to have a sponsor id except the first user. My code is
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save {email.downcase!}
validates :first_name, :presence =>true, length: {maximum: 50}
validates :last_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, length: {maximum: 255}, format: {with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false }
has_secure_password
validates :password, presence: true, length: {minimum: 6}, allow_nil:true
.
.
.
class User < ActiveRecord::Base
belongs_to :sponsor, class_name: "User"
validates :sponsor, presence:true
validate :sponsor_id_valid
def sponsor_id_valid
errors.add(:sponsor_id, "is not exist") if User.exists? sponsor_id: sponsor_id
end
#Validate that the user isV exist in the data
def validate_sponsor_id
errors.add(:sponsor_id,"is not exist") if User.find(self.sponsor_id).blank?
end
end
I require that the User need to have sponsor_id for all user exept the User.first. I tried unless method but it didn't give me a desired outcome. The way I test the code is that I create a test code which give the first user to have a sponsor_id to be nil and the test failed. Can anyone help me please?
Thanks
def sponsor_id_valid
if self.sponsor_id.blank? && self != User.first
errors.add(:sponsor_id, "is not exist")
end
end
EDIT: to take account of the case where there isn't an existing first user yet, (which we want to pass) the logic could be changed to:
if self.sponsor_id.blank? && (first_user = User.first) && (self != first_user)
#add a validation error
There's a way of validate a field only for the create action ?
Supose,
class Client < ActiveRecord::Base
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
in that case the password field will be required to save anything to db #client.save
What I want is that the password must be present only for: create the client and update the password, any other field update doesn't need password.
You can add the following to your validation.
on: :create
For your code it will be
validates :password, presence: true, length: { minimum: 6 } , on: :create
If you want the validation for multiple action then you can provide array of symbols.
On : [:create ,:edit]
I want to be able to update my user information without having to have the user set a password each time they edit any other attribute.
My current validations are:
validates :password, presence: true, length: { minimum: 8 }
validates :password_confirm, presence: true
how can I make this conditional? It would be clever to only require these validations if the password and password_confirm attribues were in the params. I could use some idea about how to achieve this. Thanks.
I think this should do it:
validates :password, presence: true, length: { minimum: 8 }
validates :password_confirm, presence: true, :if => Proc.new { |a| a.password_changed? || a.new_record? }
See the docs on ActiveModel::Dirty for more on the _changed? methods. There should be no problem running the presence validation on password every time you change an attribute since this will persist in the DB, so it will always pass (whereas password_confirm will not persist and thus requires a conditional).