Nested models and parent validation
This will probably work for you, but I have a feeling there's a much better answer out there. It sounds like a bug to me.
class Parent < ActiveRecord::Base
validate :must_have_children
def must_have_children
if children.empty? || children.all?(&:marked_for_destruction?)
errors.add(:base, 'Must have at least one child')
end
end
end
How to validate a nested model object based on the state of the parent object?
Add a virtual attribute on your Address
model:
class Address < ActiveRecord::Base
belongs_to :participant
attr_accessor :skip_validation
validates_presence_of :address1, :state, :suburb, :postcode,
:unless => :skip_validation
end
Set the virtual attribute on the address object when current_step
is set.
class Participant < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
attr_accessor :current_step
validates_presence_of :first_name, :last_name,
:if => lambda {|r| r.current_step == "name"}
def current_step=(value)
unless (value == "address")
address.skip_validation = true
end
@current_step = value
end
end
Validations Across Models and Nested Model/Object Form in Rails
edit: hum, I read your post a bit too fast, I thought that with where the child references a value in the parent
, you meant the foreign key parent_id
... My answer might still help, not sure.
I think you're looking for the inverse_of
option. Try that:
class Parent < ActiveRecord::Base
has_one :child, inverse_of :parent
end
class Child < ActiveRecord::Base
belongs_to :parent, inverse_of :child
end
From the doc:
Validating the presence of a parent model
If you want to validate that a child record is associated with a parent record, you can use validates_presence_of and inverse_of as this example illustrates:
class Member < ActiveRecord::Base
has_many :posts, :inverse_of => :member
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :member, :inverse_of => :posts
validates_presence_of :member
end
Django Nested Admin validation of nested inlines from parent forms
It appears you can still use the model.clean()
method for form validation:
from django.core.exceptions import ValidationError
class Child(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='children')
starts_at = models.DateTimeField(null=True, blank=True)
def clean(self):
parent_start = self.parent.starts_at
child_start = self.starts_at
if parent_start and child_start < parent_start:
raise ValidationError(f'This group cannot start before the season starts')
Which will make ValidationError
appear on the Child
form. Using the clean
method on the Parent
form is also possible, and would give the errors at that level.
Custom validation errors on nested models
You should keep them in the child model since that's the one validated, however, you can set conditionals with if:
and unless:
class Order < ActiveRecord::Base
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
You can do several variations on this, read more in the rails documentation http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
I guess you could add an attribute, created_by
to child, and make Child select which validations to use depending on that one. You can do that like they do in this answer: Rails how to set a temporary variable that's not a database field
How to validate existence of parent id in child model in Rails for nested attributes
If I get it right you have trouble saving associated records with your setup:
params = {number: 'order-123', order_items_attributes:{product_id:1, quantity: 2, price: 3}}
Order.create params # => this should not work
To fix it, you need to tell Rails explicitly about associations, using inverse_of
option:
class Order
# without inverse_of option validation on OrderItem
# is run before this association is created
has_many :order_items, inverse_of: :order
accepts_nested_attributes_for :order_items
end
It's not required in your case but your can add inverse_of
in OrderItem
as well:
class OrderItem
belongs_to :order, inverse_of: :order_items
# ... the rest
end
More about using inverse_of
option with associations can be read here.
Related Topics
Ruby on Rails - Access Controller Variable from Model
Where Is the Rails Method That Converts Data from 'Datetime_Select' into a Datetime Object
Uploading Multiple Files With Paperclip
Reading the Last N Lines of a File in Ruby
Set Global Default Encoding For Ruby 1.9
Why Does Ruby'S 'Gets' Includes the Closing Newline
Installing Jekyll Without Root
Why Does Adding "Sleep 1" in an After Hook Cause This Rspec/Capybara Test to Pass
Rails Model, View, Controller, and Helper: What Goes Where
Rails 2 to Rails 3:Using Link_To Instead of Link_To_Remote (Including Remote and Update)
Extract Single String from HTML Using Ruby/Mechanize (And Nokogiri)
Rails Dot Instead of Slash in Url
Ruby Array Subtraction Without Removing Items More Than Once