I have a Competition and a Competition Entry model, the former includes a form and an optional "Question" field which isn't displayed if the admin user doesn't fill it out.
The Competition Entry model includes an "Answer" field that only needs to be validated if the question is present, but I'm not sure how to achieve that - is there a way to take advantage of the belongs_to/has_many association they have?
You can make validations conditional on a method, and in that method check the associated model.
class CompetitionEntry < ActiveRecord::Base
validates :answer, :presence => true, :if => :validate_answer?
def validate_answer?
!self.competition.question.blank?
end
end
A railscast about conditional validations!
Related
I need to check presence validation for the associated attributes every time when the parent model gets updated.
In my user.rb
accepts_nested_attributes_for :histories
has_many :histories
I need to add validation for the histories when the user model gets updated, I know accepts_nested_attributes will take care of the validations while adding the user through forms, I need to check for the validation every time the user model gets updated even in the console,
If I add
validates :histories, presence: true
It will check for record in the histories table, If any record available for the user It will skip the validation for histories, I need to validate every time the object gets updated. Is there any way to validate whether the new record is being created when updating the parent model?
From your description, I think what you may be looking for is validates_associated:
class User < ApplicationRecord
has_many :histories
# validates that the association exists
validates :histories, presence: true
# validates that the objects in the associated collection are themselves valid
validates_associated :histories
end
the validates :attribute, presence: true validator is meant to validate first-class attributes on the model, not relationships. Such things like on a User class validates :email, presence: true, is where it works best.
Is your goal to validate the has_many relationship or test the relationship? If it's a test, you should make a spec that runs a test along the lines of it { should have_many(:histories) }.... obviously depending on your testing framework.
If you're goal is to validate the has many relationship, you may need to write a custom validate method. However, can you share a little more about what exactly your trying to accomplish/what about the has_many relationship you are trying to validate?
I have a model A which accepts nested attributes for model B. The number of model B entries and whether an entry is required or not is decided based on a 3rd table in db. I use form_for to generate a form with fields_for for model B entries. This is how the validation needs to work:
An entry for model B is allowed to be blank if its corresponding :required field is false, but it should not be saved, i.e, this entry should not raise validation error but should be rejected by :reject_if.
An entry whose :required field is true is not allowed to be blank. These entries should not be rejected by the :reject_if but raise a validation error when it is being saved to db.
class modelA < ApplicationRecord
has_many :modelBs, dependent: :destroy
accepts_nested_attributes_for :modelBs, reject_if: :not_required_and_blank?
def not_required_and_blank?
# return true if modelB.entry is blank && modelB.required_entry? is false
end
end
class modelB < ApplicationRecord
belongs_to :modelA
validates :modelB_entry, presence: true, if: :required_entry?
def required_entry?
ModelC.find(:id).required
end
end
Should I do this
in the :reject_if option of accepts_nested_attributes_for (in modelA class) and in validates_presence_of modelB_entry method (in modelB class) OR
everything in :reject_if OR
everything in :validates_presence_of ?
Which gets executed first :reject_if or vaildation?
reject_if and validations have different purpose.
suppose you have a nested model modelB with two fields name and title, with name as mandatory field.
Use of reject_if
In the controller #new action, you decide to build the first nested object yourself to give user a better UI experience so that he can easily see what else he can add to the form. or the user himself clicks on the "+ Add more" button. But then decides to leave modelB blank and not entering any info in it. Now, if you don't use reject_if but you have validations the user will get the error message saying field is blank, and he won't be able to submit the form, unless he clicks on remove button.
So, in this case you will check if all fields are blank in the reject_if, if that is the case we can ignore the record, else let the model do it's work.
Also, keep in mind that reject_if does not guarantees data integrity. As any typical RoR app has multiple entry points.
In my opinion, you should go with the first option. Use both reject_if and validations
I have two models, one Post and Genre. I'm creating new post with given genre_id. What is the best way of validating if genre with given id exists? For now I'm validating presence of genre_id, but it's not enough.
validates :genre_id, presence: true
I know I can check whether the genre exists in a controller, but I would prefer to have this in my post validator object.
You can explicitly tell Rails to validate the genre association and not just the attribute, genre_id with:
has_one :genre
validates_presence_of :genre
validates_presence_of
I have a 1-to-1 association between 2 mongoid models and I keep getting duplicates, that is having more than one child record(card) with same parent_id(that is user). I have tried validating uniqueness of the belongs_to association has shown below, but it doesn't work.
class User
include Mongoid::Document
field :name, type: String
has_one :card
end
The second model:
class Card
include Mongoid::Document
field :name, type: String
belongs_to :user
validates :user, :uniqueness => {:scope => :user_has_child}
def user_has_child
q = Segment.where(drop_id: {'$ne' => nil})
s = q.map(&:drop_id)
errors.add(:drop_id, "this user already has a card") if s.include?(:drop_id)
end
end
The syntax is more simple. You just want to make sure there are no 2 documents with the same user_id
class Card
belongs_to :user
validates_uniqueness_of :user
You need to use scope if you want the uniqueness of a tuple of n fields. For example, if a User can have at most one card per year, you can write
class Card
field :year
belongs_to :user
validates_uniqueness_of :user, scope: [:year] # A user can have one card per year
Note that validations apply when you save the model ie. you try to persist the changes. Calling .save will return true or false if some validations fail, but the object in memory is always modified! This is so, for example, you can display previous values in the HTML input fields, so the user knew what he wrote and can fix it (otherwise he'd have to re-write all his information in case of a single mistake)
Also, Mongoid by default handles dirty tracking (this is now the doc for v5.0 but it was the same for Mongoid 4). That is to say, you can call .changed? .changes, etc on the object in memory to see what are the changes compared to the object in the DB.
I have a Category and a Post model, with each Post belonging to a Category. Before creating or updating a post, I need to check that the category selected exists. What's the best way to validate this information?
At the moment, I'm doing a find in the controller to ensure that the category exists. Is it possible to put these kinds of validations in the model?
http://blog.hasmanythrough.com/2007/7/14/validate-your-existence
class Post < ActiveRecord::Base
belongs_to :category
validates_presence_of :category
end
-OR-
class Post < ActiveRecord::Base
belongs_to :category
validates :category, presence: true
end
Rails versions prior to 3.2:
class Post < ActiveRecord::Base
belongs_to :category
validates_existence_of :category
end
In Rails 3.2, validates_existence_of is replaced by validates_presence_of.
I've put this in my model:
validate :ensure_category_exists
def ensure_category_exists
errors.add('Category') unless self.blog.categories.find_by_id(self.category_id)
end
Which prints "Category is invalid" if the category does not exist for the parent blog.
It's definitely worth mentioning my experiences. This is for Rails 4 (potentially other versions as well).
Given an entity has_many or has_one of a model.
Validation that will ensure the entered association (association ID) exists, even if there is an ID given in the submission.
validates_presence_of :model
IS NOT THE SAME as a validation that will ensure there is something entered (not blank) in the input.
validates_presence_of :model_id
You may be able to get by with just the former, but I have both to have more specific error messages.
In my way of thinking a better choice is this gem: https://github.com/perfectline/validates_existence
It validates the related model's existence in the database. Imagine you have a dropdown field that gives back some garbage data even when you do not select anything (default non selected first field label as value). Validating presence won't work, as it will pass for existing data. But we want some kind of a constraint and this DB side check is what solves the problem.
In rails 5 and above, belongs_to automatically validates for presence.
But if you use belongs_to :category, optional: true it does not validate presence, and you can then do post.update!(category: -1) which is not great. To fix that:
validates :category, presence: true, if: :category_id
Just to be clear, the above is useful only when the association is optional.
In Rails 3, validates_associated is probably what you're looking for?
http://guides.rubyonrails.org/active_record_validations_callbacks.html#validates_associated