SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    mouse monkey
    Join Date
    Dec 1999
    Location
    UK
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [rails] Polymorphic Associations

    I'm planning an application that would store different types of content. Some attributes would be used by all content types (title, description etc.), other attributes would be specific to the content type (eg. a photo would have a thumbnail_url).

    I'd like to avoid single table inheritance, because additional content types are likely to be added overtime so im uncomfortable adding additional attributes to the content table which wont be used by all entities.

    Therefore, I'd like a base table that stores the attributes shared by all content types, and then additional tables for the extended data.

    For example the base 'content' table would contain the following rows:

    Content Table
    Code:
    title
    description
    created_on
    extended_type
    Then the 'article', 'photo' and 'audio' tables would contain the extended data for each appropriate type.

    Article Table
    Code:
    full_text
    Photo Table
    Code:
    full_url
    thumbnail_url
    Audio Table
    Code:
    length
    artist
    album
    From what I've read this appears to be possible using Rails' polymorphic associations but I'm a bit confused on how to implement this. Would this be achieved using something familiar to the following?

    Code:
    class link < ActiveRecord::Base
      belongs_to :content
      belongs_to :extendedinfo, :polymorphic => true
    end
    
    class Content < ActiveRecord::Base
      has_one :link
      has_one :extendedinfo, :through => :link
    end
    
    class Article < ActiveRecord::Base
      has_one :link, :as => :extentioninfo
      has_one :content, :through => :link
    end
    
    class Photo < ActiveRecord::Base
      has_one :link, :as => :extentioninfo
      has_one :content, :through => :link
    end
    
    class Audio < ActiveRecord::Base
      has_one :link, :as => :extentioninfo
      has_one :content, :through => :link
    end
    Am I on the right track here, or is there another way of achieving similar? Thanks.

  2. #2
    SitePoint Enthusiast thebasti's Avatar
    Join Date
    Aug 2004
    Location
    Novi Sad, Serbia
    Posts
    60
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    You are nearly there.

    First of all, as far as I can see, you don't need link model/table. Each Content entry will be related to only one Article, Photo, or Audio entry. So it's enough to add two columns to Content table:

    extendedinfo_id
    extendedinfo_type

    There's a tutorial doing something similar to what you need on Rails Wiki - HowToUsePolymorphicAssociations.

    That being said, I am not sure that you really need separate models for each type of content. If I understand correctly, each content type will basically act the same and the only difference will be what data set they have. Generally speaking you need different models only when those models need to act differently. I know that you don't want to use STI, but there's an article 'STI abuse' that discusses when creating new model is a good thing. In you case you might want to create one model with type column which you can then use to know which attributes model has.

    Of course this all depends on what you really want to achieve and what's the "big picture", in some cases it's good to have separate models (e.g. if they will become more different over time).

    Hope this helps.
    "Strategy without tactics is the slowest route to victory.
    Tactics without strategy is the noise before defeat."
    - Sun Tzu
    Icebergist - a web dev blog | Orange Iceberg Web App Development

  3. #3
    mouse monkey
    Join Date
    Dec 1999
    Location
    UK
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for your help.

    I read HowToUsePolymorphicAssociations before posting but that caused some additional confusion as it's dealing with one-to-many relationships rather than one-to-one relationships.

    So would my content table look like the following:

    Content Table
    Code:
    id
    title
    description
    created_on
    extended_id
    extended_type
    with a content base class:

    models/content.rb
    Code:
    class Content < ActiveRecord::Base
    	has_one :extended, :polymorphic => true
    end
    Would the Image class then extend ActiveRecord::Base with a belongs_to :content, or would it extend the Content class?

    models/image.rb
    Code:
    class Image < ActiveRecord::Base
    	belongs_to :content
    end
    or

    models/image.rb
    Code:
    class Image < Content
    end

  4. #4
    SitePoint Enthusiast thebasti's Avatar
    Join Date
    Aug 2004
    Location
    Novi Sad, Serbia
    Posts
    60
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Fletch,

    Sorry, I looked more closely at your code and I realized that you have got it wrong. You turned around what relation needs to be polymorphic.

    You need something like:

    Code Ruby:
    class Content < ActiveRecord::Base
    	belongs_to :extended, :polymorphic => true
    end
     
    class Image < ActiveRecord::Base
    	has_one :content, :as => :extended
    end

    You should also take a look (if you didn't already) at Rails API explanation of Polymorphic Associations (there's a separate section).
    "Strategy without tactics is the slowest route to victory.
    Tactics without strategy is the noise before defeat."
    - Sun Tzu
    Icebergist - a web dev blog | Orange Iceberg Web App Development

  5. #5
    mouse monkey
    Join Date
    Dec 1999
    Location
    UK
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Excellent! Thank you very much for your help hebasti, I have things up and running now.

    I presume you can't eager load the extended information when finding all content items eg...

    Can you eager load polymorphic relationships when finding from the base class? For example...

    Code:
    Content.find(:all, :include => :extended)
    The above doesn't seem to work (which is understandable) but you can eager load the additional information when finding a certain content type.

    Code:
    Image.find(:all, :include => :content)
    Not a huge problem anyway, the pages will be heavily cached.

  6. #6
    SitePoint Enthusiast thebasti's Avatar
    Join Date
    Aug 2004
    Location
    Novi Sad, Serbia
    Posts
    60
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As far as I know you can't eager load the polymorphic relationships when finding from the base class. As you said, it's understandable since you don't know upfront what tables you should join it to.
    "Strategy without tactics is the slowest route to victory.
    Tactics without strategy is the noise before defeat."
    - Sun Tzu
    Icebergist - a web dev blog | Orange Iceberg Web App Development


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •