设置多态has_many:通过关系

Ser*_*dis 36 polymorphism ruby-on-rails-3

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
Run Code Online (Sandbox Code Playgroud)

我已经创建了我的模型,如上面的代码所示.文章将是许多可以有标签的模型之一.类别模型将包含可能分配的所有类别.标记模型将是一个多态连接表,表示标记的关系.

class Article < ActiveRecord::Base
  has_many :tags, :as => :taggable
  has_many :categories, :through => :taggable
end

class Category < ActiveRecord::Base
  has_many :tags, :as => :taggable
  has_many :articles, :through => :taggable
end

class Tag < ActiveRecord::Base
  belongs_to :taggable, :polymorphic => true
  belongs_to :category
end
Run Code Online (Sandbox Code Playgroud)

我似乎无法让它工作,我可以做非多态,但我必须有多态的部分.有任何想法吗?

编辑:仍然没有做到这一点:

class Article < ActiveRecord::Base
    has_many :taggables, :as => :tag
    has_many :categories, :through => :taggables, :source => :tag, :source_type => "Article"
end
class Category < ActiveRecord::Base
    has_many :taggables, :as => :tag
    has_many :articles, :through => :taggables, :source => :tag, :source_type => "Article"
end
class Tag < ActiveRecord::Base
  belongs_to :taggable, :polymorphic => true
  belongs_to :category
end
Run Code Online (Sandbox Code Playgroud)

Ser*_*dis 88

要创建多态has_many:through,必须先创建模型.我们将使用'Article','Category'和'Tag',其中'Tag'是连接模型,Article是可以用类别"标记"的许多对象之一.

首先,您要创建"文章"和"类别"模型.这些是基本模型,不需要特别注意,但是:

rails g model Article name:string
rails g model Category name:string
Run Code Online (Sandbox Code Playgroud)

现在,我们将创建我们的多态连接表:

rails g model Tag taggable_id:integer taggable_type:string category_id:integer
Run Code Online (Sandbox Code Playgroud)

join-table将两个表连接在一起,或者在我们的例子中通过多态行为将一个表连接到许多其他表.它通过存储来自两个单独表的ID来实现.这会创建一个链接.我们的"类别"表将始终为"类别",因此我们包含"category_id".它链接的表格各不相同,因此我们添加了一个"taggable_id"项,它包含任何可标记项的ID.然后,我们使用'taggable_type'来完成链接,允许链接知道它链接到什么,例如文章.

现在,我们需要设置我们的模型:

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
Run Code Online (Sandbox Code Playgroud)

之后,使用以下方法设置数据库:

rake db:migrate
Run Code Online (Sandbox Code Playgroud)

而已!现在,您可以使用真实数据设置数据库:

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."
Run Code Online (Sandbox Code Playgroud)

现在你有几个类别和各种文章.但是,它们不使用标签进行分类.所以,我们需要这样做:

a = Tag.new
a.taggable = Article.find_by_name("Picking the right restaurant.")
a.category = Category.find_by_name("Food")
a.save
Run Code Online (Sandbox Code Playgroud)

然后,您可以为每个重复此操作,这将链接您的类别和文章.完成此操作后,您将能够访问每篇文章的类别和每个类别的文章:

Article.first.categories
Category.first.articles
Run Code Online (Sandbox Code Playgroud)

笔记:

1)每当你想删除链接模型链接的项目时,请确保使用"destroy".当您销毁链接对象时,它也会破坏链接.这可确保没有坏链接或死链接.这就是为什么我们使用':dependent =>:destroy'

2)在设置我们的"文章"模型时,它是我们的"可标记"模型,它必须使用:as链接.由于在前面的例子中我们使用'taggable_type'和'taggable_id',我们使用:as =>:taggable.这有助于rails知道如何在数据库中存储值.

3)当将类别链接到文章时,我们使用:has_many:articles,:through =>:tags,:source =>:taggable,:source_type =>'Article'这告诉类别模型应该有多个:文章到:标签.源是:可标记的,原因与上述相同.source-type是"Article",因为模型会自动将taggable_type设置为自己的名称.

  • 您能否在迁移中包含最佳索引? (2认同)

Ger*_*rry 15

你根本无法使连接表具有多态性,至少Rails不支持这种开箱即用.解决方案是(取自Obie的Rails 3方式):

如果您真的需要它,has_many :through可以使用多态关联,但只能通过准确指定您想要的多态关联类型.为此,您必须使用该:source_type选项.在大多数情况下,您必须使用该:source选项,因为关联名称与用于多态关联的接口名称不匹配:

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"
Run Code Online (Sandbox Code Playgroud)

它是冗长的,如果你走这条路线,整个方案就会失去它的优雅,但它有效:

User.first.commented_timesheets
Run Code Online (Sandbox Code Playgroud)

我希望我帮忙!