I'm migrating an app from rails 2.3 to rails 3. I'm planning on moving to heroku, so I'm also moving to postgres.
I have a model which has lots of fields - about 30. I want to validate the completion of all of these fields, except 2. In my rails 2 app I had:
validates_presence_of (self.column_names - ["id", "email_not_name"]), :on => :update
This worked, and in fact works in Rails3 as well. The problem comes when I try to run the migrations for the new database - I get the "PGError: ERROR: relation “table_name” does not exist" error described here. Not sure why this doesn't occur with SQLite3, but it doesn't really matter.
If I remove the validation, the migrations run fine - the problem is that none of the self.column_names actually exist yet. Similarly, if I change the migration to
validates_presence_of :field1, :field2, :on => :update
the migration will run without problems. Clearly, I could just list all 30 fields, but this strikes me as clumsy and not very maintainable. What I really want is:
validates :all, :except=>:email_not_name, :presence=>true, :on => :update
but unfortunately that doesn't actually exist! Is there a way that I can do this without resorting to this? (either rails2 or rails3 style)
Answer:
Ok, so for anyone who comes across this, here's how to do this. The solution is to create a custom error handler, called with:
validate :check_all_questions_completed, :on => :update
The error handler itself is:
def check_all_questions_completed
Person.column_names.each do |col|
if (Person.column_names - ["id", "email_not_name"]).include?(col) && send(col).nil?
errors.add(col)
end
aend
end
If anyone can tidy up my code, please do (the model concerned is Person)
Related
EDIT: I realized the comments about there not being a difference with :save already covered were correct, and was able to work through some errors. However, it still appears the regex validation is failing for :password.
I'm a little confused, and think the problem might be related to there being only :password_digest in the table itself, while we use password and password_confirmation as attributes in the model. But I'm also guessing Rails has this all worked out and takes care of everything with the has_secure_password function.
validates_format_of :password, :with => /\A[a-zA-Z]\z/, :on => :create, :update fails with the string password.
Obviously, this doesn't make sense and matches fine in the console (using =~ or .match(). Also notice if I set allow_nil: true for :password other tests involving the user start failing (which doesn't make sense, as the password should never be nil anyways).
I don't see any use case where you need on create and update together if on: :save is already there.
For more model callbacks please refer this.
According to rails docs
The default behavior for all the built-in validation helpers is to be run on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use on: :create to run the validation only when a new record is created or on: :update to run the validation only when a record is updated.
So, you should not use :on option in your validation
Rails 4.1.16 or 4.2.7
Lets, for example, we have
class Foo < ActiveRecord::Base
validate :name, presence: true
end
Then in IRB we will get
Foo.create # => validation error
Foo.transaction{ Foo.create } # COMMIT
I can figure out, that there is no raise in transaction block, it mean, that it will be committed (on course I can use create!, but it does not cancel the question). But validation fails! IMHO this behaviour is not less than strange.
Can you clarify, why it was made in this way? May be I'm do not understood some specific in this case?
It will be perfect, if you know how to make ActiveRecord failing loudly, with no sense of !(bang) at the and of the command.
Thank you!
I have an account model like so
class Account < ActiveRecord::Base
end
I had some duplicates records on it in production so I've added this line to it
validates_uniqueness_of :owner_id, :on => :create
so it will only verify this on new records and NOT on save!
it works on localhost and even on console on production.
but I have a job that runs every 10 minutes that uses rails runner and it fails for
Validation failed: Owner has already been taken (ActiveRecord::RecordInvalid)
The runner does some actions on accounts and then saves them.
am I doing something wrong on this syntax ?
Only time I've seen this happen was when a controller "create" action was being sent as a :get request (instead of :post as it should be in REST). The result was the validation didn't realize it was doing a :create. Doesn't sound like what you've got here... but maybe similar?
FYI my fix at the time was to use if: :new_record? instead of on: :create until the request type could be fixed. You could try this.
Also, you can put a debug statement into the validation to interrogate the state of the object during that validation with e.g. if: -> { binding.pry } or if: -> { debugger } (depending on what you use to debug with, of course). Hopefully that will help figure out what the runner is doing different. You can ask the object if it's a new_record? or what changes it has to save, etc.
I have found that this method has no
:on => :create
only :if
so I've did this:
validates_uniqueness_of :owner_id, :if => Proc.new { |account| account.created_at > Time.now - 10.seconds }
I need to validate the membership card number of a table to be unique only for current year, so I must build a custom validator for my model column.
My questions are:
Is heavy to make a query to check for uniqueness in current year every time I update/create a single row? How am I expected to deal
with this?
I used custom validators only on CakePHP, can you post a stupid example only to show me a syntax example?
Edit 1:
Notice that the current year is stored in a column called expire_date which is a year-month-day date format.
The check should be performed only for the year however.
How can I deal with this? That's why I suppose I should use a custom validator, I think scope shouldn't work in this case.
Edit 2:
I just noticed a :if option, I'll check if I can implement this through it.
Rails 3 provides syntax sugar, so you can do something like this:
validates :car_number, :uniqueness => {:scope => :expire_year, :message => 'has already been taken this year'}, :on => :update
def expire_year
expire_date.strftime("%Y")
end
How the query heavy is depends on DB schema and load. Don't forget to setup an index on year column and check the rails logs.
Note this will work for update only, because new object doesn't have the timestamps populated until save.
ActiveRecord already have that!
validates_uniqueness_of :car_number, :scope => :year
Docs for validates_uniqueness_of
When my model instance is created, it generates an "invoice_no". This invoice_no depends on the financial_year of the invoice which is derived from the invoice_date given in the form.
Now i validate the presence of invoice_date. Only after the invoice_date is valid, can I generate the financial_year and invoice_no.
Now what would be the best way to validate for the uniqueness of invoice_no?
1. validates :invoice_date, :presence => true
2. before_create :assign_financial_year # Based on a valid invoice_date
3. before_create :generate invoice_no # Based on a valid financial_year
4. validates :invoice_no, :uniqueness => {:scope => [:financial_year, :another_field], :case_sensitive => false}
I've already put a unique index in the database on this table based on all the relevant fields.
What would be the best way to mix step 2 and 3 above with validations on step 1 and 4?
or Should I not bother on the uniqueness validation in rails since it's a generated number and already handled in the database? If I don't put this validation, what would be a graceful way to handle exception if raised due to uniqueness violation if ever it is generated?
I'm fairly experienced with Rails and have thought of a few ugly ways already. Just want more opinions on strategy from other experienced Rails programmers.
I don't think you need to necessarily calculate the year after the validation has run. You could do it beforehand and fail gracefully if there's no invoice_date. That way, your validation will still run and you can try again later once it's present.