3
votes

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.

2
Really? changing your "type" column from a string to an integer is the difference between success and failure? Have you profiled this? - Nick Messick
The big question is why you want to do this? The type column is a string for a reason. Why do you want it to return an integer? and why for just Guest? - Taryn East
@TarynEast That's just an example. We've build a social community and relate any trigger to a notification table, that could be user, activity, comment, participation, observation, private_message etc... The number of notifications or news are currently about 40 millions. - motionless
We try to save 1/4 of data storage space. I think it is worth to spend some time of investigation. - motionless
Cool, you've added the explanation - its always a Good Idea to do that - just in case there's an assumption you're working under that maybe you hadn't spotted. In this case your explanation makes sense, but I've often found that when people explain why they're asking for a fix - that actually there's a better way to do it that they hadn't thought of, that's why I always ask :) - Taryn East

2 Answers

3
votes

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'
0
votes

Have you tried:

class CreateNotification < ActiveRecord::Migration
  def change
    create_table :notifications do |t|
      t.integer :notifiable_id
      t.integer  :notifiable_type_id # should be a tinyint at the database
      t.timestamps
    end
  end
end

Rails will assume that if you call it; "notifiable_type" then it IS a type.

but if you call it notifiable_type_id then rails will assume it is an integer id belonging to a model called notifiable_type.

You may need to add a notifiable_type model too and include ids for each of the classes you have... or it might Just Work... but that's definitely where I'd start.