rails validate depending on form - ruby-on-rails

I have two separate forms for a profile picture and the rest of the profile information. Both forms, however, correspond to the profile model. For several of the profile attributes, I have validations like:
validates :title, presence: true
validates :zip_code, presence: true
The problem is that the validations are checked when someone tried to upload an image, which I don't want. That being said, I also have an image validator, so I don't want to avoid validation completely, just certain ones. I was thinking of trying to access the params hash in the model, but I can't figure out how and I'm pretty certain its a bad idea anyway. How can I make the right validation conditions? I already tried this:
validates :title, presence: true, :unless => :picture_exists?
def picture_exists?
if self.pic
puts 'yo pic exist'
return true
else
puts 'yo no pic'
return false
end
end
but it does not work because it checks whether or not the profile has a picture, not whether the params have a picture. So if someone had already saved a picture, they would be able to bypass the validations which I don't want. I want the validations to be bypassed when they are not using the picture submit form.

You can approach the issue in several ways:
1.- Skip all validations in your controller action (and validate manually, I guess)
save(validate: false) (source)
2.- Use a condition that you set manually before saving like this.
3.- Use a custom validation that stops all other validations from triggering if passes.
Maybe you can come up with more.
PS: Why would you expect your user to bypass the other validations before setting the profile picture?
GL & HF

Related

Cannot edit a User record using Rails Admin. Email is already taken

What would cause me not to be able to edit a User record using Rails Admin?
I can edit every other record fine, but when I try to edit this particular record I get this error:
User failed to be updated
- Email has already been taken
I'm using Devise for user signups, along with this model validation:
validates :email, uniqueness: { case_sensitive: false }
I go to the user edit form in Rails Admin, change some other field and on submit get the error above. It's really strange and I'm trying to see if there may be any ideas I may have missed.
Why would this validation trigger when I'm just updating the record? There is only one record with that particular email value, and that's the one I'm editing.
Try limiting your validation to create and see whether that makes a difference.
validates :email, uniqueness: { case_sensitive: false }, on: :create
Your code for validating email is right.
You got the default validation message "Email has already been taken", because you have the same email in your Users Table.
It's either part of a seed file so you don't remember saving it.
Or has been saved by other user.
Please try to search it, like:
User.find_by(:email => "test_mail_here")
# so you can check duplication ef email.
The default validation of your code is for: create & update
You can use :
validates :email, uniqueness: { case_sensitive: false }, on: :create
But, I think much better NOT to used it.
Best solution for me, is update or delete the duplicate email. So you can use the full functionality of validates method again.

Selecting all current attributes of a model for validations in Rails

When setting up my models I often find myself having to write out all of its attributes when setting up certain validations. A common example is when I use the presence parameter:
validates :first_name, :last_name, :username, :email, presence: true
Is there a clever way to select all of its attributes without explicitly writing them all out similar to how you can retrieve them in the rails console?
User.columns
And pass it as an argument in the validates method?
ALL_ATTRIBUTES = User.columns
validates ALL_ATTRIBUTES, presence: true
Trying something like this out I got this error undefined method 'to_sym'
I will NOT encourage you or anyone to do this. Reason being when you run into issues, when an object of your model doesn't get saved and throw errors because of a new column which was added to application after some time in future, and you or new developers will wonder WHY?!?!.
However, if you must do then here you go:
validates *self.column_names.map(&:to_sym), presence: true
Here, * in Ruby is known as splat operator and here's the explanation on &:.
This is an awful idea. But you can do it this way:
attrs = column_names.map { |column| column.to_sym }
validates *attrs, presence: true
Why is it a bad idea? Because it's not very clear what is being validated it. It makes debugging hard, and could cause you have strange bugs. If you add a column in the future that does not require presence validation, you will trip up. Also, some things my not require presence. For example, an email field will need a regex validation, which automatically knows that a blank string is invalid. So a presence validator is redundant.
Beware of being too clever, as it's sometimes not so clever after all.

Devise: Unable to limit model validation to specific def's

So I'm working on the registration aspect of the site currently. I have a main sign up which is just full name, email and password. (aka new.html.erb)
After you fill in that information I direct you to a new site (setup.html.erb) and ask for more info like city, country etc.
On that you also have the edit profile account.
I am trying to make my app more secure and adding restrictions and presence etc in the model. However how can I limit them.
Currently if I do
validates :email, presence: true,
and I go to a form that doesn't even contain the email for nor permits it I get an error up that I need to add an email.
Also how do I fix this: I make presence true, I input require in html5. But still if I go to my source code and just remove the form and push submit it saves and I can bypass adding info.
Currently if I do validates :email, presence: true,
and I go to a form that doesn't even contain the email for nor permits it I get an error up that I need to add an email.
Fix:
what you need is a conditional validation. If we look at rail guides it says
Sometimes it will make sense to validate an object only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string, a Proc or an Array.
So in your model you could do something like:
class Order < ActiveRecord::Base
validates :email, presence: true, if: :need_to_validate?
def need_to_validate?
#your condition to check whether you want email validation or not
end
end
Update:
You can use params[:action] and params[:controller] smartly to check in which action and controller(hence which view) you currently are in so your method would be:
def need_to_validate?
params[:action] == your_view_action && params[:controller] == your_controller_name #your condition to check whether you want email validation or not
end

Make rails ignore inputs if user is admin

I have a user object, he can update his profile which includes name, user_name, password (blank), password_confirmation (blank), email, email_confirmation (blank), bio and picture url.
My model states that all of the (blank) MUST be filled in. but if your admin and your just going to the users page to update the user's role - You as the admin should not have to fill in user data you obviously don't know.
So how does one get around this? should I instead create a list of users with a drop down beside them? is this not, essentially , a giant form? If so - how would this get created?
essentially: What's the best way to deal with this situation?
This is currently how users get updated
def update
#user = User.friendly.find(params[:id])
#user.update_attributes(user_update_params)
if #user.save
render :show
else
render :edit
end
end
private
def user_update_params
params.require(:user).permit(:name, :user_name, :email, :email_confirmation, :password,
:password_confirmation, :bio, :picture_url, :role)
end
The real problem seems to be that you have a logical error in your User model validations.
You seem to have a validation of the form,
validates :password, presence: true, confirmation: true
which is enforced EVERY TIME, i.e. a new password has to be selected every single time a user object is saved. But this is likely not what you want. You likely want this validation to only be enforced when the user is created for the first time, i.e. when it is a new record.
You can do this with,
validates :password, presence: true, confirmation: true, if: :new_record?
update_attribute
Updates the attribute without doing validations, you need this one.
check out this api doc
EDIT:
Speaking about reading documentation
Here is an abstract from the method documentation
update_attribute(name, value) public
Updates a single attribute and saves the record. This is especially
useful for boolean flags on existing records. Also note that
Validation is skipped.
Callbacks are invoked.
updated_at/updated_on column is updated if that column is available.
Updates all the attributes that are dirty in this object.
EDIT:
If you still need to validate with this method, note that it says that callbacks are invoked, so what you can do is write your own code to validate input and use callbacks as described here.

How can I skip a specific validation when importing data?

How can I skip a specific model validation when importing data?
For example, suppose I have this model:
class Account
validates :street_address, presence: true
end
Normally, I don't want accounts to be saved without addresses, but I'm also going to convert a lot of data from an old system, and many accounts there don't have addresses.
My goal is that I can add the old accounts to the new database, but in the future, when these accounts are edited, a street address will have to be added.
Clarification
As I said, I want to skip a specific validation; others should still run. For example, an account without an account number shouldn't be loaded into the new system at all.
This should work:
class Account
attr_accessor :importing
validates :street_address, presence: true,
unless: Proc.new { |account| account.importing }
end
old_system_accounts.each do |account|
# In the conversion script...
new_account = Account.new
new_account.importing = true # So it knows to ignore that validation
# ... load data from old system
new_account.save!
end
If you're only going to do the conversion one time (i.e, after importing the old data you won't need to do this again), you could just skip validations when you save the imported records instead of modifying your app to support it.
new_account.save validate: false
note that
account.update_attribute(:street_address, new_address)
will skip validations as well. #update_attributes (notice the 's') run validations, where update_attribute (singular) does not.

Resources