Monday, March 8, 2010

Polymorphic Associations in Rails

I've already done this once before, but I didn't blog it, so I forgot it. (You have to teach something in order to really learn it).


Essentially: The "belongs_to :arbitrary_name" means what you're going to call all of the different things to which this item can belong. It's arbitrary. The "has_many" of the owner has to include "as :arbitrary_name"


Let's say you're doing asynchronous uploads using cors_uploads and PaperClip and you don't know what something is going to be attached at the time you upload it.

Scenario:
Professors can create assignments and attach several files are necessary for students to have in order to complete the assignment.

Likewise, Students can upload multiple files with an assignment.

When they add a file it begins to upload immediately.
When the file completes a callback adds its ID to a hidden field in the form.
If the form is submitted before the uploads are complete the callback updates the upload with the assignment ID instead of adding its ID to the form.
If the browser window is about to be closed it will warn the user if uploads are still in progress.

One solution is to have a model for student uploads and another for professor uploads... but that leads to a possibly messy file system as you add more and more types of uploads.

Instead we create one model for assets which can belong_to anything "attachable". We define that assignments and submissions can have many assets as an attachable.

class Asset < ActiveRecord::Base
    belongs_to :attachable, :polymorphic => true
  end

  class Assignment < ActiveRecord::Base
    has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use.
  end 
 
class Submission < ActiveRecord::Base
    has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use.
  end
 
 
# Then something like 
if params[assignment]
@asset.attachable = Assignment.find(params[assignment][:id])
elsif params[submission]
@asset.attachable = Submission.find(params[submission][:id]) 
end 
# Or
@assignment.assets << Asset.find(params[asset][:id])  

Rails Documentation (see the Polymorphic Association section):
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

Links to this post:

Create a Link

<< Home