ruby on rails has_many validations - ruby-on-rails

Lets say there is an error in shirt. will the error appear also in person.errors ?
and if so, how can i reach it ? ( i don't want to use person.shirt.errors)
class Person < ActiveRecord::Base
has_one : shirt
has_many : pants
validates :name, :presence => true
validates_length_of :name, :minimum => 3
end
person = Person.new(:name => "JD")
person.shirt.create(:color=> "red")
person.pants.create(:type=> "jeans")
person.valid?

According to this post it seems errors on child entities will be copied to the parent during a save, see here -> Validations section although this might have been changed since
Validations simply work as you'd expect;
#valid? will also validate nested models,
#save(false) will save without validations, etc.
The only thing to note is that all error messages
from the nested models are copied to the parent errors
object for error_messages_for. This will probably change
in the future, as discussed on the ticket, but that's
outside of the scope of this patch.

It should load into "person.errors". You can reach it by calling
person.errors.at(:<replace_this_with_name_of_attribute>)
You can also call
person.errors.each { |attr, msg| puts "attr = '#{attr}', msg = '#{msg}'" }
to check for all the error attribute names and the corresponding error messages. Good luck!

Related

How to tell which of the associated models fails my ActiveRecord validation?

Say I have the following models:
class Race < ApplicationRecord
has_many :horses
end
class Horse < ApplicationRecord
belongs_to :race
validates :name, presence: true
end
Now with my REST API I'm creating a Race object and associate multiple horses. One of the Horses fails validation, which adds the error.
Adding an error means adding entries to errors.details and errors.messages, where errors is a field of the Race model. Both these fields are hashes, with horses.name as a key and details of the error(s) and error message(s) as values, respectively.
I'm looking for a way to find, which of the associated Horse models failed the validation so that I can provide a comprehensive error message. A reference, id, or even an index, would be enough.
race = Race.create race_params
race.errors.messages
=> {'horses.name' => ['Can't be blank']}
race.horces[0].errors.messages
=> {'name' => ['Can't be blank']}
to get records with error, simply filter race.horses
with_error = race.horses.select{|h| h.errors.messages.present?}
index = race.horses.index( with_error[0] )

ActiveRecord validates inclusion in list - list isn't updated after new associated model created

I have a Company model and an Employer model. Employer belongs_to :company and Company has_many :employers. Within my Employer model I have the following validation:
validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}
I'm running into a problem where the above validation fails. Here is an example setup in a controller action that will cause the validation to fail:
company = Company.new(company_params)
# company_params contains nested attributes for employers
company.employers.each do |employer|
employer.password = SecureRandom.hex
end
company.employers.first.role = 'Admin' if client.employers.count == 1
company.save!
admin = company.employers.where(role: 'Admin').order(created_at: :asc).last
admin.update(some_attr: 'some_val')
On the last line in the example code snippet, admin.update will fail because the validation is checking to see if company_id is included in the list, which it is not, since the list was generated before company was saved.
Obviously there are ways around this such as grabbing the value of company.id and then using it to define admin later, but that seems like a roundabout solution. What I'd like to know is if there is a better way to solve this problem.
Update
Apparently the possible workaround I suggested doesn't even work.
new_company = Company.find(company.id)
admin = new_company.employers.where(role: 'Admin').order(created_at: :asc).last
admin.update
# Fails validation as before
I'm not sure I understand your question completely, but there is an issue in this part of the code:
validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}
The validation is configured on the class-level, so it won't work well with updates on that model (won't be re-evaluated on subsequent validations).
The docs state that you can use a block for inclusion in, so you could try to do that as well:
validates :company_id, inclusion: {in: ->() { Company.pluck(:id).prepend(nil) }}
Some people would recommend that you not even do this validation, but instead, have a database constraint on that column.
I believe you are misusing the inclusion validator here. If you want to validate that an associated model exists, instead of its id column having a value, you can do this in two ways. In ActivRecord, you can use a presence validator.
validates :company, presence: true
You should also use a foreign key constraint on the database level. This prevents a model from being saved if there is no corresponding record in the associated table.
add_foreign_key :employers, :companies
If it gets past ActiveRecord, the database will throw an error if there is no company record with the given company_id.

Rails conditional validation in model

I have a Rails 3.2.18 app where I'm trying to do some conditional validation on a model.
In the call model there are two fields :location_id (which is an association to a list of pre-defined locations) and :location_other (which is a text field where someone could type in a string or in this case an address).
What I want to be able to do is use validations when creating a call to where either the :location_id or :location_other is validated to be present.
I've read through the Rails validations guide and am a little confused. Was hoping someone could shed some light on how to do this easily with a conditional.
I believe this is what you're looking for:
class Call < ActiveRecord::Base
validate :location_id_or_other
def location_id_or_other
if location_id.blank? && location_other.blank?
errors.add(:location_other, 'needs to be present if location_id is not present')
end
end
end
location_id_or_other is a custom validation method that checks if location_id and location_other are blank. If they both are, then it adds a validation error. If the presence of location_id and location_other is an exclusive or, i.e. only one of the two can be present, not either, and not both, then you can make the following change to the if block in the method.
if location_id.blank? == location_other.blank?
errors.add(:location_other, "must be present if location_id isn't, but can't be present if location_id is")
end
Alternate Solution
class Call < ActiveRecord::Base
validates :location_id, presence: true, unless: :location_other
validates :location_other, presence: true, unless: :location_id
end
This solution (only) works if the presence of location_id and location_other is an exclusive or.
Check out the Rails Validation Guide for more information.

Validations that rely on associations being built in Rails

A Course has many Lessons, and they are chosen by the user with a JS drag-n-drop widget which is working fine.
Here's the relevant part of the params when I choose two lessons:
Parameters: {
"course_lessons_attributes"=>[
{"lesson_id"=>"43", "episode"=>"1"},
{"lesson_id"=>"44", "episode"=>"2"}
]
}
I want to perform some validations on the #course and it's new set of lessons, including how many there are, the sum of the lessons' prices and other stuff. Here's a sample:
Course Model
validate :contains_lessons
def contains_lessons
errors[:course] << 'must have at least one lesson' unless lessons.any?
end
My problem is that the associations between the course and the lessons are not yet built before the course is saved, and that's when I want to call upon them for my validations (using course.lessons).
What's the correct way to be performing custom validations that rely on associations?
Thanks.
looks like you don't need a custom validation here, consider using this one:
validates :lessons, :presence => true
or
validates :lessons, :presence => {:on => :create}
You can't access the course.lessons, but the course_lessons are there, so I ended up doing something like this in the validation method to get access to the array of lessons.
def custom validation
val_lessons = Lesson.find(course_lessons.map(&:lesson_id))
# ...
# check some things about the associated lessons and add errors
# ...
end
I'm still open to there being a better way to do this.

Is validates_presence_of the preferred technique to require a has_many relationship

Basically: My model requires at least one instance of an associated model be present. Should I use validates_presence_of to assert this validation, or should I write some custom validation code?
Here are the essentials of my model:
class Event < ActiveRecord::Base
has_and_belongs_to_many :channels
validates_presence_of :channels, :message => "can't be empty"
end
(I assume things would be the same if I used has_many in place of has_and_belongs_to_many.)
Instead of the validates_presence_of line I could do this:
def validate
errors.add(:channels, "can't be empty") if channels.size < 1
end
I replaced the latter with the former in the Rails app I'm working on and am wondering if there might be any problems.
So to be more sure, I wrote the following rspec coverage, and both implementations respond the same:
describe Event do
before do
#net = Factory.create(:network)
#net_config = Factory.create(:network_config, :network => #net)
end
it "must have a channel" do
e = Factory.build(:event, :network => #net, :channels => [])
e.should have(1).error_on(:channels)
end
end
That is, if I remove the validation code, the above spec fails; if I put in either version of the validation code, the above spec passes.
So I might assume that my new implementation is ok. But I've read that validates_presence triggers a database load which, in turn, would wipe out any in-memory objects constructed from nested attributes. The proxy_target method, on the other hand, will return the in-memory objects without triggering a load. Some links on proxy_target: http://rubydoc.info/docs/rails/ActiveRecord/Associations/AssociationProxy http://withoutscope.com/2008/8/22/don-t-use-proxy_target-in-ar-association-extensions
In my particular case I'm not using ActiveRecord::Relation, but I wonder if I need to be cautious about this.

Resources