Rails Custom Validation - Only One Record Can Be True

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:

  1. both servers instantiate a new unsaved model in memory, and populate its fields
  2. both read from the DB, to see if the uniqueness validation passes
  3. there is no record yet with that patient_id and doctor_id pair, the validation passes in both processes
  4. both servers save the record, and the data is written to the the DB
  5. 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



Leave a reply



Submit