The accepted answer does not solve the original problem. Jose wanted 2 things:
1) To ensure that the Parent always has at least one child
and
2) To be able to delete all children when the Parent is deleted
You do not need any before_destroy
callbacks to prevent the deletion of a child.
I wrote a detailed blog post describing the solution, but I'll cover the basics here as well.
The solution includes various ingredients: the use of presence validation and nested attributes in the Parent model, and making sure that the method that deletes the child doesn't call .destroy
on the child, but that the child is deleted from the Parent model via nested attributes.
In the Parent model:
attr_accessible :children_attributes
has_many :children, dependent: :destroy
accepts_nested_attributes_for :children, allow_destroy: true
validates :children, presence: true
In the child model:
belongs_to :parent
Next, the easiest way to allow children to be deleted, except for the last one, is to use nested forms, as covered in Railscasts #196. Basically, you would have one form with fields for both the Parent and the Children. Any updates to the Location, as well as the Children, including the deletion of children, would be processed by the update
action in the Parent Controller.
The way you delete a child via nested forms is by passing in a key called _destroy
with a value that evaluates to true. The allow_destroy: true
option we set in the Parent model is what allows this. The documentation for Active Record Nested Attributes covers this, but here's a quick example that shows how you would delete a Child whose id
equals 2
from its Parent:
parent.children_attributes = { id: '2', _destroy: '1' }
parent.save
Note that you don't need to do this yourself in the Parent Controller if you're using nested forms as in Railscasts #196. Rails takes care of it for you.
With the presence validation in the Parent model, Rails will automatically prevent the last child from being deleted.
I think that at the time Jose posted his question, the presence validation was not working the way it was supposed to. It wasn't fixed until July of 2012 with this pull request, but that was almost 2 years ago. Seeing dbortz post his outdated solution 12 days ago made me realize that there is still confusion about this issue, so I wanted to make sure to post the correct solution.
For an alternate solution that doesn't use nested forms, see my blog post: http://www.moncefbelyamani.com/rails-prevent-the-destruction-of-child-object-when-parent-requires-its-presence/
Parent -> Child
relation be named:children
? I'm quite sure rails will understand that you're pointing to theChild
model anyway. – Kurtkurth