Validate existence of submodel in 'has_one' relation - ruby-on-rails

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

Related

mongoid-4 how to validate uniqueness of belongs_to in 1 to 1 association

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.

Validation of HABTM association on delete:

I have a newbie rails question. I'm trying to make sure a model has at least one association via a HABTM relationship. Basically I have created the following validation:
validate :has_tags?
def has_tags?
errors.add(:base, 'Must have at least one tag.') if self.tags.blank?
end
This works fine when I create a new record. The problem is when I take the model and try to remove the association, doing something like this:
tag = Tag.find(params[:tag_id])$
#command.tags.delete(tag)$
It is permitted, i.e. the association will be deleted. Based on my reading on HABTM associations (http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association), I should "use has_many :through if you need validations, callbacks, or extra attributes on the join model."
I guess my question is how to perform validation on the .delete method for an association. Should I do this manually when I call delete (i.e. run a separate join to count the number of associations before executing a delete), or is there a way to use a validation model when deleting? Here is my model:
class Command < ActiveRecord::Base
has_many :tagmapsorters
has_many :tags, through: :tagmapsorters
validates :text, presence: true
validates :description, presence: true
validates :text, uniqueness: true
validate :has_tags?
def has_tags?
errors.add(:base, 'Must have at least one tag.') if self.tags.blank?
end
end
I appreciate you taking the time to help me.
Dan
Any callbacks that you need should be registered as before_destroy (for validations) or after_destroy (for cleanup) on the join model Tagmapsorter, as that is the record that is actually being destroyed.

validating uniqueness of has_many association with a string "checksum"

I had an idea to validate the uniqueness of a has_many association: what if we generate a string based on the ids of the associated records?
For example:
class Exam
has_many :problems #problems are unique and can be in multiple exams
validate :checksum, uniqueness: true #string
before_validate :check
def check
checksum = problems.map {|p| p.id}.join
end
end
The edge case we want to solve is:
Given distinct problems 3x4, sqrt(4), 5+5, etc.., we don't want all of them to be in more than one exam.
Does anyone have thoughts on this approach? Is there a better way to validate uniqueness of has_many?
(P.S. I'm not sure if "checksum" is the right term.)
Base on the following:
You have an Exam model and a Problem model
Each Exam has_many Problems
Each problem must be unique per Exam
I think it makes more sense to place a uniqueness validation on an attribute of Problem but scope the validation to its Exam so that that multiple Exams can have the same Problems but each Exam has a unique set of Problems.
So for example if there was an attribute named value, we place the uniqueness validation on it and scope it to exam_id.
class Exam < ActiveRecord::Base
has_many :problems
end
class Problem < ActiveRecord::Base
belongs_to :exam
validates :value, :uniqueness => { :scope => :exam_id }
end

Validating a conditionally displayed field in Rails

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!

Rails: Validating existence of an association

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

Resources