Support :dependent => :restrict and :dependent => :nullify
I would love the :dependent option of AR associations to be more flexible and support :restrict and :nullify values. The first meaning that a record may not be destroyed while dependent records still exist and the second meaning that the foreign key of the dependent record is set to nil when the record in question is destroyed.
:restrict has proven to be useful in many scenarios and is also supported in sql. Furthermore, this option may improve database compatibility if ever Rails will aim to utilize database consistency checking features based on model definitions
A case for the :nullify (or :nillify) option is categories that are nested by a parent_id column where you want subcategories to become root categories when their supercategory is destroyed.
Also nullifying a foreign_key is in any case better than leaving it pointing to a non-existent record because the latter is sure to produce exceptions and may even produce an erroneous association if ever the primary key of the dependent record is reused.
If I'm not completely mistaken, this has been implemented by now. Rails raises an exception when trying to delete a record which depends on other records:
"ActiveRecord::DeleteRestrictionError in XYZController#destroy
Cannot delete record because of dependent zyxs"
The :restrict option would be very useful
+3 for that option! in DRY design is ugly to have to "embed" records validation in code or create SQL statements for that!
Jon Dahl commented
ActiveRecord really should implement this. It's almost a no-brainer.
1. It's common. Every project should maintain referential integrity in some way, and :dependent => :destroy isn't always appropriate. Who wants to do a cascading delete from roles to users, or manufacturers to products, or order_statuses to orders? I don't think I've ever worked on a project where cascading deletes were always appropriate. Any lookup table, at minimum, needs this feature.
2. It fits with the Rails philosophy. Rails says "Let your application handle referential integrity, not the database". But without :dependent => :restrict, one of the most important pieces of referential integrity is missing.
3. It's easy. 9 lines of code to add this to has_many. Check out this gist:
Adminmattetti (Admin, Rails) commented
Rafael Bueno, please try to post in English, not everyone can read Portuguese.
For those you can't, Rafael created a workaround to add this kind of validation.
Rafael Bueno commented
Eu criei uma outra forma para fazer essa válidação.
1 - adicionei o seguinte código na minha pasta LIB
# Classe com a minha exceção
class ErrorRelashionShip < RuntimeError; end
# Inclui os métodos de válidação restrict
# Restriction de relacionamento
if self.method(relashionship_class.class.to_s.pluralize.downcase).call.length > 0
raise ErrorRelashionShip, "O registro não pode ser removido, pois têm relacionamentos"
2 - Adicionei no método :before_remove da minha model, lembrando que funciona em has_and_belongs_to_many e has_many
has_and_belongs_to_many :users, :uniq => true, :before_remove => :restrict_delete
has_many :users, :before_remove => :restrict_delete
3 - Adicionei o seguinte código no meu controller
4 - Alterei o método destroy do controller
@functionality = Functionality.find(params[:id])
flash[:notice] = 'Funcionalidade deletada com sucesso'
rescue ModelsHelper::Exceptions::ErrorRelashionShip => error
flash[:notice] = error.message
I would really like to see this in Rails. Wouldn't be better, however, implement it as a validation?
Heh, make that +3 :P
+1 for the :restrict option :)
Stefan Kroes commented
:dependent => :nullify already exists on has_many associations. The :restrict options would still be very useful though.