Setting up a polymorphic has_many :through relationship

后端 未结 2 422
轻奢々
轻奢々 2020-12-02 05:14
rails g model Article name:string
rails g model Category name:string
rails g model Tag name:string taggable_id:integer taggable_type:string category_id:integer


        
相关标签:
2条回答
  • 2020-12-02 06:01

    You simply cannot make the join table polymorphic, at least Rails does not support this out of the box. The solution is (taken from Obie's Rails 3 way):

    If you really need it, has_many :through is possible with polymorphic associations, but only by specifying exactly what type of polymorphic associations you want. To do so you must use the :source_type option. In most cases you will have to use the :source option, since the association name will not match the interface name used for the polymorphic association:

    class User < ActiveRecord::Base
      has_many :comments
      has_many :commented_timesheets, :through => :comments, :source => :commentable,
               :source_type => "Timesheet"
      has_many :commented_billable_weeks, :through => :comments, :source => :commentable,
               :source_type => "BillableWeek"
    

    It's verbose and the whole scheme loses its elegance if you go this route, but it works:

    User.first.commented_timesheets
    

    I hope I helped!

    0 讨论(0)
  • 2020-12-02 06:09

    To create a polymorphic has_many :through, you must first create your models. We will use'Article,' 'Category,' and 'Tag' where 'Tag' is the join-model and Article is one of many objects which can be "tagged" with a category.

    First you create your 'Article' and 'Category' models. These are basic models which do not need any special attention, just yet:

    rails g model Article name:string
    rails g model Category name:string
    

    Now, we will create our polymorphic join-table:

    rails g model Tag taggable_id:integer taggable_type:string category_id:integer
    

    The join-table joins together two tables, or in our case one table to many others via polymorphic behavior. It does this by storing the ID from two separate tables. This creates a link. Our 'Category' table will always be a 'Category' so we include 'category_id.' The tables it links to vary, so we add an item 'taggable_id' which holds the id of any taggable item. Then, we use 'taggable_type' to complete the link allowing the link to know what it is linked to, such as an article.

    Now, we need to set up our models:

    class Article < ActiveRecord::Base
      has_many :tags, :as => :taggable, :dependent => :destroy
      has_many :categories, :through => :tags
    end
    class Category < ActiveRecord::Base
      has_many :tags, :dependent => :destroy
      has_many :articles, :through => :tags, :source => :taggable, :source_type => 'Article'
    end
    class Tag < ActiveRecord::Base
      belongs_to :taggable, :polymorphic => true
      belongs_to :category
    end
    

    After this, setup your database using:

    rake db:migrate
    

    That's it! Now, you can setup your database with real data:

    Category.create :name => "Food"
    Article.create :name => "Picking the right restaurant."
    Article.create :name => "The perfect cherry pie!"
    Article.create :name => "Foods to avoid when in a hurry!"
    Category.create :name => "Kitchen"
    Article.create :name => "The buyers guide to great refrigeration units."
    Article.create :name => "The best stove for your money."
    Category.create :name => "Beverages"
    Article.create :name => "How to: Make your own soda."
    Article.create :name => "How to: Fermenting fruit."
    

    Now you have a few categories and various articles. They are not categorized using tags, however. So, we will need to do that:

    a = Tag.new
    a.taggable = Article.find_by_name("Picking the right restaurant.")
    a.category = Category.find_by_name("Food")
    a.save
    

    You could then repeat this for each, this will link your categories and articles. After doing this you will be able to access each article's categories and each categorie's articles:

    Article.first.categories
    Category.first.articles
    

    Notes:

    1)Whenever you want to delete an item that is linked by a link-model make sure to use "destroy." When you destroy a linked object, it will also destroy the link. This ensures that there are no bad or dead links. This is why we use ':dependent => :destroy'

    2)When setting up our 'Article' model, which is one our 'taggable' models, it must be linked using :as. Since in the preceeding example we used 'taggable_type' and 'taggable_id' we use :as => :taggable. This helps rails know how to store the values in the database.

    3)When linking categories to articles, we use: has_many :articles, :through => :tags, :source => :taggable, :source_type => 'Article' This tells the category model that it should have many :articles through :tags. The source is :taggable, for the same reason as above. The source-type is "Article" because a model will automatically set taggable_type to its own name.

    0 讨论(0)
提交回复
热议问题