Search code examples
ruby-on-railsactiverecordparent-childparentbelongs-to

Limiting an object to one ownership ID


I have a Flag model that has the possibility of belonging to a Topic, Post, or Review. With that being said the Flag model has all three of their corresponding IDs.

class Flag < ActiveRecord::Base
  belongs_to :topic
  belongs_to :post
  belongs_to :review
end

class Topic < ActiveRecord::Base
  has_many :flags
end

class Post < ActiveRecord::Base
  has_many :flags
end

class Review < ActiveRecord::Base
  has_many :flags
end

When it comes down to flagging for, let's say, a post how do I ensure that no other ID (topic_id, review_id) can exist thereafter? I thought of creating specific flag models for each of the three but that route seems too repetitive. Would a model validation be my only sensible choice? If so what would it look like? More importantly am I going about this association setup in the right way?


Solution

  • What about using polymorphic associations?

    Try with:

    class Flag < ActiveRecord::Base
      belongs_to :flaggable, polymorphic: true
    end
    
    class Topic < ActiveRecord::Base
      has_many :flags, as: :flaggable
    end
    
    class Post < ActiveRecord::Base
      has_many :flags, as: :flaggable
    end
    
    class Review < ActiveRecord::Base
      has_many :flags, as: :flaggable
    end
    
    
    class UpdateAssociationsOnFlags < ActiveRecord::Migration
      def change
        remove_index  :flags, :topic_id
        remove_column :flags, :topic_id
    
        remove_index  :flags, :post_id
        remove_column :flags, :post_id
    
        remove_index  :flags, :review_id
        remove_column :flags, :review_id
    
    
        add_column :flags, :flaggable_id, :integer
        add_column :flags, :flaggable_type, :string
    
        add_index :flags, :flaggable_id
      end
    end
    

    Edit, in reply to the question author's comment:

    You can just add a different association:

    class Flag < ActiveRecord::Base
      belongs_to :flaggable, polymorphic: true
      belongs_to :reporter, class_name: 'User', foreign_key: :reporter_id
    end
    
    class User < ActiveRecord::Base
      has_many :reported_flags, class_name: 'Flag', foreign_key: :reporter_id
    end
    
    class AddUserForeignKeyToFlags < ActiveRecord::Migration
      def change
        add_column :flags, :reporter_id, :integer
        add_index :flags, :reporter_id
      end
    end