validates_associated model with condition - ruby-on-rails

I have the following validates_associated scenario
class Parent
include Mongoid::Document
validates_associated :son
validates_associated :daughter
end
when i create a parent, either of son or daughter is only created not both.
Now my problem is, when i try to create parent with son, then validation fails due to daughter validation and vice versa.
Is there any way that i can validate only son when son parameters are posted or or validate only daughter when daughter parameters are posted
Thanks

You can supply an :if option and test if the associated document exists:
class Parent
include Mongoid::Document
validates_associated :son, :if => Proc.new { |p| p.son.present? }
validates_associated :daughter, :if => Proc.new { |p| p.daughter.present? }
end

Why don't you use an associated child object, which has an attribute (i.e. gender) if it's beeing a son or a daughter.
Child model (male or female, depending on value in gender):
class Child
include Mongoid::Document
field :gender, :type => Symbol
# and more fields as you probably want
embedded_in :parent, :inverse_of => :child
# your validation code
def son?
gender == :male
end
def daughter?
gender == :female
end
end
will be Embedded in Parent model:
class Parent
include Mongoid::Document
embeds_one :child
validates_associated :child
end

Related

Validation to prevent parent referencing self as children

I'm looking for a solution to prevent "parents" to add his self as "children".
My Model looks like this:
class Category < ActiveRecord::Base
belongs_to :parent, :class_name => 'Category'
has_many :children, :class_name => 'Category', :foreign_key => 'parent_id'
end
Now I look for a solution to prevent things like this
parent = Category.create(name: "Parent")
Category.new(name: "Children", parent_id: parent.id).valid? # should be => false
You can add a custom validation for that.
Something like
class ParentValidator < ActiveModel::Validator
def validate(record)
if record.parent_id == record.id
record.errors[:parent_id] << 'A record\'s parent cannot be the record itself'
end
end
end
class Category
include ActiveModel::Validations
validates_with ParentValidator
end
or even simpler (if it is a one off thing)
class Category < ActiveRecord::Base
validate :parent_not_self, on: :save
def parent_not_self
if parent_id == id
errors.add(:parent_id, 'A record\'s parent cannot be the record itself')
end
end
end
Both cases will generate a validation error when you try to assign the record itself as the parent's record

Raise validation error from virtual attribute in Rails

I got two models: Source and SourceType. Source of course belongs to SourceType.
I want to create new source and assign proper sourcetype object to it. 'Proper' means that one virtual attribute of the source object match some of sourceType objects test regexpression, which becomes source's Type.
I got an attribute writer in source object
class Source < ActiveRecord::Base
belongs_to :source_type
def url=(value)
SourceType.each do |type|
# here i match type's regexp to input value and if match,
# assign it to the new source object
end
end
end
I don't want to build any custom validator for it'll be need to run through SourceTypes twice. How to raise validate error if no sourcetypes is fit to the input so user could see error reasons in a form?
Validation
If you set the virtual attribute using attr_accessor, you should be able to validate on the model you're sending data to (alternatively using inverse_of if you'd like to validate on the nested model):
http://api.rubyonrails.org/classes/ActiveModel/Validator.html
This can now be used in combination with the validates method (see ActiveModel::Validations::ClassMethods.validates for more on this).
class Person
include ActiveModel::Validations
attr_accessor :title
validates :title, presence: true
end
Code
I'd do this:
class Source < ActiveRecord::Base
belongs_to :source_type, inverse_of: :sources
attr_accessor :url
end
class SourceType < ActiveRecord::Base
has_many :sources, inverse_of: :source_type
validates :source_type_attr, presence: { if: :url_match? }
def url_match?
self.sources.url == [your_regex]
end
end

Validating models against the parent model

I have two models, one is the parent of the other, and the parent accepts_nested_attributes_for and validates_associated the children.
However, some of my validations have an :if that needs to check one of the properties of the parent.
I was thinking that I could do something like this:
validates_presence_of :blah, :if => Proc.new{|thing| thing.parent.some_value.present?}
However, the 'parent' relationship doesn't appear to be setup at the time of validation (I would assume the children get instantiated and validated first.
Therefore is there any way of doing what I'm thinking of? Is it possible?
You can use before_update or before_create callbacks as per your need like this..
def before_update
self.errors.add("Error Message") if self.parent.some_value.present?
return false if self.errors.count > 0
end
def before_create
self.errors.add("Error Message") if self.parent.some_value.present?
return false if self.errors.count > 0
end
This kind of validation should work:
validates_associated :children
But it won't
The reason is, as far as I understand, beause using acceptes_nested_attributes_for is creating nested objects straight to database via one transaction without passing any children validations.
What you can do here: write your own validation in parent model and validate creating children objects.
Use the :inverse_of option for the association on the parent, so the children will have a reference to the parent when they are built.
class Parent < ActiveRecord::Base
has_many :children, :inverse_of => :parent
accepts_nested_attributes_for :children
end
class Child < ActiveRecord::Base
belongs_to :parent
end
p = Parent.new :children_attributes => { 0 => { :child_attribute => 'value' } }
p.children.first.parent #=> shouldn't be nil anymore

Rails setting OR conditions in validate_presence_of in a model?

In a rails model, is it possible to do something like
class Example < ActiveRecord::Base
#associations
validates_presence_of :item_id, (:user_id OR :user_email)
#functions
end
Where the model has 3 columns of :item_id, :user_id, and :user_email?
I want the model to be valid as long as I have a :user_id or a :user_email.
Idea being that if the item is recommended to a person who isn't currently signed up, it can be associated via email address for when the recommended person signs up.
Or is there a different method that I can use instead?
One approach is to wrap those fields as a virtual attribute, say:
class Example < ActiveRecord::Base
validates_presence_of :referral
def referral
user_id || user_email
end
end
or you can just throw a custom validate validation method. See custom validations on the Rails API
If both user_id and user_email come from another model, perhaps it's better to add the association instead
class Example
belongs_to :user
validates_associated :user
before_validate :build_user_from_id_or_email
def build_user_from_id_or_email
# ... Find something with the parameters
end
end
validates_presence_of :item_id
validates_presence_of :user_id, :if => Proc.new{ |x| x.user_email.blank? }
validates_presence_of :user_email, :if => Proc.new{ |x| x.user_id.blank? }

Rails AR validates_uniqueness_of against polymorphic relationship

Is it posible to validate the uniqueness of a child model's attribute scoped against a polymorphic relationship?
For example I have a model called field that belongs to fieldable:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :name, :scope => :fieldable_id
end
I have several other models (Pages, Items) which have many Fields. So what I want is to validate the uniqueness of the field name against the parent model, but the problem is that occasionally a Page and an Item share the same ID number, causing the validations to fail when they shouldn't.
Am I just doing this wrong or is there a better way to do this?
Just widen the scope to include the fieldable type:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :name, :scope => [:fieldable_id, :fieldable_type]
end
You can also add a message to override the default message, or use scope to add the validation:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :fieldable_id, :scope => [:fieldable_id, :fieldable_type], :message => 'cannot be duplicated'
end
As a bonus if you go to your en.yml, and enter:
activerecord:
attributes:
field:
fieldable_id: 'Field'
You are going to replace the default 'subject' that rails add to the errors with the one you specify here. So instead of saying: Fieldable Id has been already taken or so, it would say:
Field cannot be duplicated

Resources