In the Rails 3 timeframe, I would like Rails to...

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.

337 votes
Vote
Sign in
Check!
(thinking…)
Reset
or sign in with
  • facebook
  • google
    Password icon
    I agree to the terms of service
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    Stefan KroesStefan Kroes shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

    10 comments

    Sign in
    Check!
    (thinking…)
    Reset
    or sign in with
    • facebook
    • google
      Password icon
      I agree to the terms of service
      Signed in as (Sign out)
      Submitting...
      • StefanStefan commented  ·   ·  Flag as inappropriate

        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"

      • mbrancambranca commented  ·   ·  Flag as inappropriate

        +3 for that option! in DRY design is ugly to have to "embed" records validation in code or create SQL statements for that!

      • Jon DahlJon Dahl commented  ·   ·  Flag as inappropriate

        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:

        http://gist.github.com/170059

      • mattettiAdminmattetti (Admin, Rails) commented  ·   ·  Flag as inappropriate

        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 BuenoRafael Bueno commented  ·   ·  Flag as inappropriate

        Eu criei uma outra forma para fazer essa válidação.

        1 - adicionei o seguinte código na minha pasta LIB

        module ModelsHelper

        # Classe com a minha exceção
        module Exceptions
        class ErrorRelashionShip < RuntimeError; end
        end

        # Inclui os métodos de válidação restrict
        module All

        include Exceptions

        # Restriction de relacionamento
        def restrict_delete(relashionship_class)
        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"
        end
        end

        end
        end

        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

        ou

        has_many :users, :before_remove => :restrict_delete

        3 - Adicionei o seguinte código no meu controller
        include ModelsHelper::Exceptions

        4 - Alterei o método destroy do controller
        def destroy
        @functionality = Functionality.find(params[:id])
        @functionality.destroy
        flash[:notice] = 'Funcionalidade deletada com sucesso'

        rescue ModelsHelper::Exceptions::ErrorRelashionShip => error
        flash[:notice] = error.message
        ensure
        redirect_to(functionalities_url)

        end

      • HiqueHique commented  ·   ·  Flag as inappropriate

        I would really like to see this in Rails. Wouldn't be better, however, implement it as a validation?

      • Stefan KroesStefan Kroes commented  ·   ·  Flag as inappropriate

        :dependent => :nullify already exists on has_many associations. The :restrict options would still be very useful though.

      Feedback and Knowledge Base