web-dev-qa-db-ja.com

ポリモーフィックなhas_many:through関係の設定

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

上記のコードに示すように、モデルを作成しました。記事はタグを持つことができる多くのモデルの1つになります。カテゴリモデルには、割り当て可能なすべてのカテゴリが含まれます。タグモデルは、タグ付き関係を表す多態性結合テーブルになります。

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

私はこれを機能させることができないようです、私はそれを非多態性にすることができますが、私は多態性の部分で何か間違っている必要があります。何か案は?

編集:これはまだ正しくありません:

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
36
Serodis

ポリモーフィックなhas_many:throughを作成するには、まずモデルを作成する必要があります。 「Article」、「Category」、「Tag」を使用します。「Tag」は結合モデルであり、Articleはカテゴリで「タグ付け」できる多くのオブジェクトの1つです。

最初に、「Article」および「Category」モデルを作成します。これらはまだ特別な注意を必要としない基本的なモデルですが、まだです:

Rails g model Article name:string
Rails g model Category name:string

次に、ポリモーフィック結合テーブルを作成します。

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

結合テーブルは、2つのテーブル、またはこの場合は1つのテーブルを、ポリモーフィックな動作によって他の多くのテーブルに結合します。これは、2つの別々のテーブルからIDを格納することによって行われます。これによりリンクが作成されます。 「Category」テーブルは常に「Category」になるため、「category_id」を含めます。リンクするテーブルはさまざまであるため、タグ付け可能なアイテムのIDを保持するアイテム「taggable_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

この後、以下を使用してデータベースをセットアップします。

rake db:migrate

それでおしまい!これで、実際のデータでデータベースをセットアップできます。

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."

これで、いくつかのカテゴリとさまざまな記事があります。ただし、タグを使用して分類されることはありません。したがって、それを行う必要があります。

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

次に、それぞれについてこれを繰り返すことができます。これにより、カテゴリと記事がリンクされます。これを実行すると、各記事のカテゴリと各カテゴリの記事にアクセスできるようになります。

Article.first.categories
Category.first.articles

ノート:

1)リンクモデルでリンクされているアイテムを削除する場合は、必ず「破棄」を使用してください。リンクされたオブジェクトを破棄すると、リンクも破棄されます。これにより、不良リンクやデッドリンクがなくなります。これが「:dependent =>:destroy」を使用する理由です

2)「タグ付け可能な」モデルの1つである「Article」モデルを設定するときは、:asを使用してリンクする必要があります。前の例では「taggable_type」と「taggable_id」を使用したため、:as =>:taggableを使用します。これはRailsデータベースに値を保存する方法を知るのに役立ちます。

3)カテゴリを記事にリンクする場合、次を使用します:has_many:articles、:through =>:tags、:source =>:taggable、:source_type => 'Article'これは、カテゴリモデルに:articles through:タグ。上記と同じ理由で、ソースは:taggableです。モデルはtaggable_typeを独自の名前に自動的に設定するため、source-typeは「Article」です。

88
Serodis

結合テーブルをポリモーフィックにすることはできません。少なくともRailsはこれをサポートしていません。解決策は(Obie's Rails 3 wayから取得)です) :

本当に必要な場合は、ポリモーフィックな関連付けで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"

これは冗長であり、このルートに行くとスキーム全体が優雅さを失いますが、機能します。

User.first.commented_timesheets

助けてくれたらいいのに!

15
Gerry