gotqn
gotqn

Reputation: 43636

How to forbid deletion if association present

I have a many to many relationship between two models as follows:

#users.rb
has_many :users_to_roles
has_many :roles, through: :users_to_roles

#users_to_roles.rb
belongs_to :user
belongs_to :role

#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles

I want to disable the deletion of roles if there are users who are "in this role". Here I have found two options who should do the work:

:restrict_with_exception causes an exception to be raised if there are any associated records :restrict_with_error causes an error to be added to the owner if there are any associated objects

but there is no example with the syntax of this and how it should work.

Could you help to make this valid:

#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles, dependent: restrict_with_exception

Upvotes: 3

Views: 3938

Answers (4)

prograils
prograils

Reputation: 2376

I made it with my classes like this:

app/models/guest_chat_token.rb

class GuestChatToken < ApplicationRecord

  has_many :chat_messages, as: :sendable, dependent: :restrict_with_exception

end

app/controllers/admin/application_controller.rb

class Admin::ApplicationController < ApplicationController
....
    rescue_from ActiveRecord::DeleteRestrictionError do |exception|
            redirect_to :back, notice:
            "Be aware: #{exception.message}."
    end
end

Upvotes: 0

Vedant Agarwala
Vedant Agarwala

Reputation: 18819

The correct rails way is to do the following:

users.rb:

has_many :users_to_roles, dependant: :destroy # don't keep the join table entry if the user is gone
has_many :roles, through: :users_to_roles

Make sure that your join does not have redundant entries (in which either column is null or orphaned).

users_to_roles.rb:

belongs_to :user
belongs_to :role

# add validations presence of both user and role
# in both model and database.

Bonus, from rails 4.2 you can add forigen_key: true in your migration for referential integrity

Now in your role (I am assuming you name your models singularly and made a typo in the question), you add this:

role.rb:

has_many :users_to_roles, dependant: :restrict_with_error
has_many :users, through: :users_to_roles

Upvotes: 0

Lex Lindsey
Lex Lindsey

Reputation: 531

Alternatively, you can rescue the exception in your controller. In this example, a contact may own interest, i.e.

  class Interest < ActiveRecord::Base
    belongs_to :contact
  end

  class Contact < ActiveRecord::Base
    has_many :interests, :dependent => :restrict
   end

Then in the controller:

def destroy
    @contact = Contact.find(params[:id])
    begin
        @contact.destroy
    rescue
        flash[:msg] = "Can't delete - owns interest"
    end

   respond_to do |format|
      format.html { redirect_to(:back) }
      format.xml  { head :ok }
    end
end

The flash message will be displayed in the calling page.

Upvotes: 2

gotqn
gotqn

Reputation: 43636

Such operations can be easily do using Callbacks. In my case, I have added the following method in my model:

# callbacks
before_destroy :check_for_users_in_this_role

def check_for_users_in_this_role
  status = true
  if self.security_users.count > 0
    self.errors[:deletion_status] = 'Cannot delete security role with active users in it.'
    status = false
  else
    self.errors[:deletion_status] = 'OK.'
  end
  status
end

Upvotes: 2

Related Questions