BinaryWebPark

Part 4 – Setting Up a Polymorphic Association Among Users, Folders, Tasks

June 17, 2013

Part 4 – Setting Up a Polymorphic Association Among Users, Folders, Tasks

If you recall the small discussion on polymorphic associations in Part 1a, you’ll remember that we wanted the ability to create a task and have the option to not associate it with a specific folder. So we needed to add polymorphic assocations to our rails application.

Sidebar: Using Git

I’ll be using the git subversion control system throughout this tutorial, but you don’t have to. Michael Hartl talks uses it throughout the Rails tutorial (available for free online) or you can search for it.

git checkout -b part-4-polymorphic-models

Creating a Single Task Model

To think about how to setup the polymorphic association, we ask “what do the other models that the task model relates to have in common?” The answer is that they can all have tasks. So we create a task model with 2 extra fields needed for the polymorphic association to work — namely, taskableid and taskabletype.

rails g model task description:text duedate:string taskable_id:integer taskable_type:string

Since taskableid and taskabletype will be queried together frequently, we add an index on them as follows.

add_index :tasks, [:taskable_id, :taskable_type]

This index will help us query our database more efficiently and get the information we need returned to us in less time.

Next we need to modify the Task model with a belongs_to line as follows:

belongs_to :taskable, polymorphic: true

So your task database migration file should look something like this:

class CreateTasks < ActiveRecord::Migration
  def change
    create_table :tasks do |t|
    t.text :description
    t.string :duedate
    t.integer :taskable_id
    t.string :taskable_type</code>
    t.timestamps
  end

  add_index :tasks, [:taskable_id, :taskable_type] end
end

And your task model file should look like this:

class Task < ActiveRecord::Base
  attr_accessible :description, :duedate
  belongs_to :taskable, polymorphic: true
end

Please note I’ve removed the :taskableid and :taskabletype attributes as we don’t need them to be accessible through mass assignment.

Modifying the Todouser Model for Polymorphic Associations

We add the following line to the Todouser model:

has_many :tasks, as: :commentable

This helps set the other side of the polymorphic association. We can now use this just like any other has_many association in Rails.

Creating the Folder Model

Finally, we create the folder model as follows.

rails g model folder name:string todouser_id:integer

Note that when generating the model included a “todouser_id” attribute. This will be a foreign key that tells Rails that a folder belongs to a user (in our specific case, an instance of the Todouser model).

Sidebar: what is a foreign key?

A foreign key is a field in a database relational table that matches a candidate key in another table. For example, in our case a todouser will be able to create many folders. The intention is that each folder that the todouser creates must be associated with a todouser that is in our todouser database table. To accomplish this, we create a foreign key in the folders table and have it relate to the primary key of the todouser table. In this way, we know which folder belongs to which todouser – those that have the same matching numerical keys.

Setting Up the Polymorphic Association With the Folder Model

Setting up the polymorphic association via the “taskable” interface is the same as the Todouser model. We add the following line to the Folder model:

has_many :tasks, as: :commentable

This helps set the other side of the polymorphic association. We can now use this just like any other has_many association in Rails. The upshot is that now folders can have many tasks, and so can users.

But there’s another relationship with a Todouser that Folder Has

A Todouser can have many folders, and a folder belongs to a specific todouser (recall the diagram from part 1a of this tutorial). Think of this as a relationship that is separate from the relationship between folders and tasks and todousers and tasks.

So we need to add the following to our Todouser model:

has_many :folders, dependent: :destroy

The dependent: :destroy command tells Rails to destroy any associated folders when we delete a Todouser.

Finally, we add the following to our Folder model:

belongs_to :todouser

At this point, your Todouser model file should look something like this:

class Todouser < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me

  # attr_accessible :title, :body
  has_many :tasks, as: :commentable
  has_many :folders, dependent: :destroy
end

And your Folder model file should look something like this:

class Folder < ActiveRecord::Base
  attr_accessible :name, :todouser_id
  has_many :tasks, as: :commentable
  belongs_to :todouser
end

Sidebar: Using Git

Finally, we commit our work to git and merge it into the master branch.

git add .

git commit -m “added polymorphic associations”

git checkout master

git merge part-4-polymorphic-models

Summary

So far we’ve added authentication to our application and created the associations and models we need. Next, it’s time to start laying the groundwork to create and edit tasks.

Resources:

I’m keeping a public repo of this application at my github account. You can download the source code there.

Sources:
  1. Agile Web Development with Rails by Dave Thomas
  2. http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
  3. http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
  4. http://stackoverflow.com/questions/7867931/is-this-a-valid-use-of-polymorphism-and-if-it-is-how-should-i-declare-this-relat