I’m going through the second video in Intermediate Rails, and I’m not sure I understand how you got polymorphic associations working with only defining one side of the relationship. All of the examples I’ve seen (section 2.9 of Active Record Associations — Ruby on Rails Guides for example), have you set it up on both sides of the association using the :as option.
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
However in Shouter it ended up like this:
class Shout < ActiveRecord::Base
belongs_to :content, polymorphic: true
end
class PhotoShout < ActiveRecord::Base
end
class TextShout < ActiveRecord::Base
end
Do you not have to set up the has_one/has_many on the other side of the relationship if you don’t want to access it from that side?
I had the same question when I was digging into the relationship. What I came to realize was that Matt’s example is sorf of an ‘inversion’ of the example given in RailsGuides. Here’s how I worked it out.
In the RG example, a Picture is ‘subordinate’ to Employee and Product, kinda like an OrderItem is subordinate to an Order… that’s what I consider the ‘classic’ setup.
In the shouter example, a Shout is actually the ‘master’ object and is not subordinate to either TextShout or PhotoShout. Since the “belongs_to … polymorphic: true” sets up the required content_type and content_id that we need to show that relationship, we’re done and we don’t need the reflective “has_many”.
As far as the ERD (Entity Relationship Diagram) is concerned, In my mind, I put Shout on the left and PhotoShout / TextShout on the right as compared to the RG diagram which has Picture on the right-hand side of the diagram.
Do you not have to set up the has_one/has_many on the other side of the relationship if you don’t want to access it from that side?
That’s correct. You only need to define the association in the direction that you want to access it. In that example, @shout.content will work, but @photo_shout.shout will not. No other problems will come from defining an association on only a single side, however.
In fact it’s usually a good idea to only define associations where you need them. If you define everything on both models, you’ll very quickly end up with 20 has_manys on User in most applications.
@JESii. I agree, that was a pretty cool way to use the belongs_to relationship. It seemed like a much better alternative to using single table inheritance.
Also, I thought that the belongs_to macro took a classname? But content appears to be used like a “ghost class” or “ghost attribute” in the belongs_to association for lack of better terms.
# shout.rb
class Shout < ActiveRecord::Base
belongs_to :content, polymorphic: true
belongs_to :user
default_scope { order("created_at DESC") }
end
schema.rb
ActiveRecord::Schema.define(version: 20130723202938) do
create_table "shouts", force: true do |t|
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "content_type"
t.integer "content_id"
end
add_index "shouts", ["content_type", "content_id"], name: "index_shouts_on_content_type_and_content_id"
add_index "shouts", ["user_id"], name: "index_shouts_on_user_id"
create_table "text_shouts", force: true do |t|
t.string "body"
end
add_index "users", ["username"], name: "index_users_on_username"
end
@Esop has a similar question to mine, so I won’t go into the same details.
From what I can tell you just whatever you pass as :content to the model will be dealt with according to the established relationship.
For instance I have the following class:
class Shout < ActiveRecord::Base
belongs_to :user
default_scope { order("created_at DESC") }
belongs_to :content, polymorphic: true
def self.text_shouts
where(content_type: 'TextShout')
end
def self.build user, content
user.shouts.build(content: content)
end
def self.build_shout_content type="text", parameters
if type== 'photo'
PhotoShout.new(parameters)
else
TextShout.new(parameters)
end
end
end
So my assumption, is it doesn’t matter what you call :content. What matters is what you assign to it. If you called it :foo, as long as you assigned something to :foo it will still make the correct associations with the data.