Search code examples
ruby-on-railsactiondestroyjoin

Deleting just a join table record in Ruby on Rails


I have the following simple models:

class Event < ActiveRecord::Base
  has_many :participations
  has_many :users, :through => :participations
end

class Participation < ActiveRecord::Base
  belongs_to :event
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :participations
  has_many :events, :through => :participations
end

What I would like to do in my view is, dependant on the current users role, delete either an event and its participation record, or just a participation record on its own.

I currently have

<%= link_to 'Delete event', event, :confirm => 'Are you sure?', :method => :delete %>

which deletes both event, and its participation. Do I need another action? or can hijack the destroy action of Event? What would it look like?

Thanks


Solution

  • Well, a hack could be something like this, in a view helper:

    def link_to_delete_event( event, participation = nil )
      final_path = participation.nil? ? event_path( event ) : event_path( :id => event, :participation_id => participation )
      link_to 'Delete event', final_path, :confirm => 'Are you sure?', :method => :delete
    end
    

    And in your view you'd use link_to_delete_event( event ) to delete an event alone and link_to_delete_event( event, participation ) to delete the participation. Your controller could be something like this:

    def destroy
      @event = Event.find(params[:id])
      unless params[:participation_id].blank?
        @event.destroy
      else
        @event.participations.find( params[:participation_id] ).destroy
      end
      redirect_to somewhere_path
    end
    

    EDIT

    To make it less of a hack you should create a nested resource for participations under events:

    map.resources :events do |events|
      events.resources :participations
    end
    

    And then you'll have to create a ParticipationsController, which could look like this:

    class ParticipationsController < ApplicationController
      before_filter :load_event
    
      def destroy
        @participation = @event.participations.find( params[:id] )
        @participation.destroy
        redirect_to( event_path( @event ) )
      end
    
      protected
    
      def load_event
        @event = Event.find( params[:event_id] )
      end
    end
    

    And the link_to helper would change to this:

    def link_to_delete_event( event, participation = nil )
      if participation
        link_to 'Remove participation', event_participation_path( event, participation ), :method => :delete
      else
        link_to 'Delete event', event_path( event ), :confirm => 'Are you sure?', :method => :delete
      end
    end