Search code examples
ruby-on-railsactiverecordpolymorphic-associations

Rails 3 polymorphic association with foreign_type column as integer


We have a certain problem with polymorphic relation in rails/activerecord. As mentioned in an other question . The reason we need this kind of polymorphic relation with a integer foreign_type column is the number of records in table, we have about 40 million record in that table an the number is raising. We try to save storage at the database server an memory consumption concerning the index handling at the database.

The question mentioned earlier is related to Rails 2 and if already tried to uses this with Rails 3 but it doesn't work. The method of the module was never called and i can't see why.

I would like to have a mapping like this with column types see in the migration class

class Notification < ActiveRecord::Base
  belongs_to :notifiable, :polymorphic => true
end
class User < ActiveRecord::Base
  attr_accessible :name
  has_many :notifications, :as => :notifiable
end
class Comment < ActiveRecord::Base
  attr_accessible :text
  has_many :notifications, :as => :notifiable
end
class Message < ActiveRecord::Base
  attr_accessible :title, :text
  has_many :notifications, :as => :notifiable
end
class Activity < ActiveRecord::Base
  attr_accessible :title, :description
  has_many :notifications, :as => :notifiable
end
class CreateNotification < ActiveRecord::Migration
  def change
    create_table :notifications do |t|
      t.integer :notifiable_id
      t.integer  :notifiable_type # should be a tinyint at the database
      t.timestamps
    end
  end
end

I would like to map Comment and User with a numeric value and save the numeric value instead of the class name as type information.


Solution

  • To make the polymorphic belongs_to working, you can do that:

    In config/initializers directory, create a file and put theses lines:

    module ActiveRecord
      # = Active Record Belongs To Polymorphic Association
      module Associations
        class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
          def klass
            type = owner.send(reflection.foreign_type)
            type.presence && type.constantize
          end
        end
      end
    end
    

    then in each model, override notifiable_type (define the hash as a constant and put it anywhere you like):

    def notifiable_type
      { 0: 'User', 1: 'Comment', 3: 'Message' }[read_attribute(:notifiable_type)]
    end
    

    and for has_many, try this (set INT_CLASS_TYPE for each model):

    has_many :notifications, :conditions => ["`notifiable_type` = ?", INT_CLASS_TYPE], :foreign_key => 'notifiable_id'