Question

Does anyone know how to do a polymorphic association in Mongoid that is of the relational favor but not the embedding one.

For instance this is my Assignment model:

class Assignment
  include Mongoid::Document
  include Mongoid::Timestamps

  field :user
  field :due_at, :type => Time

  referenced_in :assignable, :inverse_of => :assignment
end

that can have a polymorphic relationship with multiple models:

class Project
  include Mongoid::Document
  include Mongoid::Timestamps

  field :name, :type => String

  references_many :assignments
end

This throws an error saying unknown constant Assignable. When I change the reference to embed, this all works as documented in Mongoid's documentation, but I need it to be reference.

Thanks!

Was it helpful?

Solution

From Mongoid Google Group it looks like this is not supported. Here's the newest relevant post I found.

Anyway, this is not to hard to implement manually. Here's my polymorphic link called Subject.

Implementing inverse part of relation might be somewhat more complicated, especially because you will need same code across multiple classes.

class Notification
  include Mongoid::Document
  include Mongoid::Timestamps

  field :type, :type => String
  field :subject_type, :type => String
  field :subject_id, :type => BSON::ObjectId

  referenced_in :sender, :class_name => "User", :inverse_of => :sent_notifications
  referenced_in :recipient, :class_name => "User", :inverse_of => :received_notifications

  def subject
    @subject ||= if subject_type && subject_id
      subject_type.constantize.find(subject_id)
    end
  end

  def subject=(subject)
    self.subject_type = subject.class.name
    self.subject_id   = subject.id
  end
end

OTHER TIPS

Answering to an ancient post, but someone may find it useful.

Now there's also a polymorphic belongs_to:

class Action                                                                                                                           
  include Mongoid::Document                                                                                                            
  include Mongoid::Timestamps::Created                                                                                                 

  field :action, type: Symbol

  belongs_to :subject, :polymorphic => true                                                                                            
end

class User                                                                                                                             
  include Mongoid::Document                                                                                                            
  include Mongoid::Timestamps                                                                                                          
  field :username, type: String
  has_many :actions, :as => :subject   
end

class Company                                                                                                                          
  include Mongoid::Document                                                                                                            
  include Mongoid::Timestamps                                                                                                          

  field :name, type: String                                                                                                            

  has_many :actions, :as => :subject
end

Rails 4+

Here's how you would implement Polymorphic Associations in Mongoid for a Comment model that can belong to both a Post and Event model.

The Comment Model:

class Comment
  include Mongoid::Document
  belongs_to :commentable, polymorphic: true

  # ...
end

Post / Event Models:

class Post
  include Mongoid::Document
  has_many :comments, as: :commentable

  # ...
end

Using Concerns:

In Rails 4+, you can use the Concern pattern and create a new module called commentable in app/models/concerns:

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end
end

and just include this module in your models:

class Post
  include Mongoid::Document
  include Commentable

  # ...
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top