Rails custom validation - Only one record can be true
You also need to check against the ID if the record is already persisted. Otherwise, saving the active game again would not succeed because there is an existing active game, which happens to be itself.
validate :only_one_active_game
scope :active, where(:active => true)
protected
def only_one_active_game
return unless active?
matches = Game.active
if persisted?
matches = matches.where('id != ?', id)
end
if matches.exists?
errors.add(:active, 'cannot have another active game')
end
end
Rails: Allow only one record of a rails model to have a boolean property true?
ActiveRecord has some update attributes methods that don't trigger callbacks like post.update_column
, Post.update_all
, etc. So you can use these in a callback like
before_save :set_primary
private
def set_primary
Post.where.not(id: id).update_all(primary: false)
end
Rails - Validation :if one condition is true
You can use a lambda for the if:
clause and do an or condition.
validates :description, presence: true, if: -> {current_step == steps.first || require_validation}
Rails validate only one enter per pair of db_columns
Rails makes this kind of validations fairly easy.
The first step is to define the validation on your model.
The uniqueness validation supports a scope
option, that should contain the name of the other column you want to limit the uniqueness to (or an Array of other columns, if it's a >=3
column scoped uniqueness).
Your mistake is to declare it with a different name (:recommend
).
This is what you want:
class DoctorRecommendation < ActiveRecord::Base
belongs_to :patient, class_name: "User"
belongs_to :doctor, class_name: "User"
validates :patient, presence: true
validates :doctor, presence: true
validates :patient_id, uniqueness: { scope: :doctor_id }
end
Considering that you already have :presence
validations for the associated models, the :uniqueness
validation can be limited to the IDs.
This will enable the validation on the application layer, that is, it will be verified in your Ruby process.
Unfortunately this is not enough in a real world scenario, where you can have multiple processes/threads modify the same table at the same time.
Imagine, for example that two requests reach your servers at the same time, both to create the same DoctorRecommendation
. If the requests are served by two server processes/threads in parallel (or close enough), there is a chance that the Rails validation will pass in both cases.
In details:
- both servers instantiate a new unsaved model in memory, and populate its fields
- both read from the DB, to see if the uniqueness validation passes
- there is no record yet with that
patient_id
anddoctor_id
pair, the validation passes in both processes - both servers save the record, and the data is written to the the DB
- bang. your uniqueness constraint has been violated.
For this reason you need to enforce uniqueness on the DB layer as well, with a unique multi-column index.
More precisely, with:
class AddMultiIndexToDoctorRecommendations < ActiveRecord::Migration
def change
# using a custom name to make sure it's below the length limit
add_index :doctor_recommendations,
[:patient_id, :doctor_id],
unique: true,
name: 'index_docrecomm_on_patient_id_and_doctor_id'
end
end
This will define a [:patient_id, :doctor_id]
index, with a unique constraint.
If you read the docs on multi column indexes (e.g. for postgres or mysql), you'll find that the order of the columns matters. The Migration I wrote uses the right order for the validation I defined above, which means that the validation queries will be correctly optimized. Make sure to modify the index if you want to invert the validation scope.
Now, back to the example above, on point 4 both server processes will try to save the record at the same time, but one will finish a few milliseconds before the other. The second will raise a ActiveRecord::RecordNotUnique
exception, that you can rescue in your code.
For example:
begin
DoctorRecommendation.create(attributes)
rescue ActiveRecord::RecordNotUnique
# ops, let's retry again, to see if we get a validation error
end
Rails Validate Field is True, If Present
You're looking for the acceptance validation.
You can either use it like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
or with further options, like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { message: 'must be abided' }
end
[edit]
You can set the options you expect the field to be as well, as a single item or an array. So if you store the field inside a hidden attribute, you can check that it is still "accepted" however you describe accepted:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: ['yes', 'TRUE'] }
end
Does rails custom validation fail on return true?
It depends how you are using your custom validations.
If you are using Active Record Callbacks, i.e. before_save
or before_validation
, if you return false
from any of these, your validation will FAIL and the record will not be saved:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#module-ActiveRecord::Callbacks-label-before_validation-2A+returning+statements
However if you are using a custom validator such as mentioned here: http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations the return value is not significant (It is not mentioned in the docs, as you say), what matters is if you add to the errors array.
How do I validate an association value is present in Rails?
Please try using custom validation
class User < ApplicationRecord
validate :company_aum_id_present, if: :provider?
private def company_aum_id_present
self.errors[:aum_id] << 'Assets Under Management is required.' if company && company.aum_id.blank?
end
end
Also When usin custom validator you dont need to include ActiveModel::Validations
as it is already included by ApplicationRecord
How to handle dependant validations on Rails?
I think there is no perfect solution unless you write all your validations as custom methods. Approach I use often:
class Product < ApplicationRecord
validates :name, presence: true, length: { is: 10 }
validate :name_custom_validator
private
def name_custom_validator
return if errors.include?(:name)
# validation code
end
end
This way you can add as many validations to :name
and if any of them fails your custom validator won't execute. But the problem with this code is that your custom validation method must be last.
Related Topics
How to Determine Leap Year in Ruby
Using Will_Paginate Without :Total_Entries to Improve a Lengthy Query
Cross-Platform Means of Getting User's Home Directory in Ruby
Set Default Stage with Capistrano 3
Bundle Command Not Found Windows X64
How to Run and Debug Ruby on Rails from Visual Studio Code
Mechanize How to Get Current Url
Why Does Date Exist in Ruby Before It Is Required
How to Remove All Elements That Satisfy a Condition in Array in Ruby
How to Detect the End of a Method Chain in Ruby
Passenger: Cannot Load Such File Rubygems/Builder
Automatically Precompile Assets Before Pushing to Heroku
How to Install a Gem Globally Without Sudo Using Rbenv
Rails Change Routing of Submit in Form_For