34

更新についてはコメントを参照してください。

私はこれについて明確で率直な答えを得るのに苦労してきました。今回はそれを得られることを願っています! :D もちろん、Rails についてはまだまだ学ぶべきことがたくさんありますが、直面している問題は理解できているので、さらに助けていただければ幸いです。

  • 「タスク」というモデルがあります。
  • 「ターゲット」と呼ばれる抽象モデルがあります。
  • Target のサブクラスの複数のインスタンスを Task に関連付けたいと思います。
  • 単一テーブルの継承を使用していません。
  • ポリモーフィックな関係をクエリして、Target のサブクラスの混合結果セットを返したいと考えています。
  • Target のサブクラスの個々のインスタンスを照会して、関連するタスクを取得したいと考えています。

したがって、Task と Target のサブクラスとの間の多対多の多態的な関係が適切であると考えています。より詳細には、コンソールで (もちろん他の場所でも) 次のようなことができるようになります。

task = Task.find(1)
task.targets
[...array of all the subclasses of Target here...]

しかし!モデル「店舗」、「ソフトウェア」、「オフィス」、「乗り物」が存在すると仮定すると、これらはすべて「ターゲット」のサブクラスであり、関係を逆方向にトラバースすることもできます。

store = Store.find(1)
store.tasks
[...array of all the Tasks this Store is related to...]
software = Software.find(18)
software.tasks
[...array of all the Tasks this Software is related to...]

ポリモーフィックなリレーションシップによって暗示されるデータベース テーブルは、このトラバーサルを実行できるように見えますが、ポリモーフィックなリレーションシップの精神を打ち負かす答えを見つけようとする際に、繰り返し発生するテーマがいくつか見られます。

  • 私の例を引き続き使用すると、人々は Store、Software、Office、Vehicle を Task で定義したいと考えているように見えますが、これは 1 つのタイプのモデルしか返さないため、ポリモーフィックな関係ではないことがすぐにわかります。
  • 最後のポイントと同様に、タスク内の店舗、ソフトウェア、オフィス、および車両を一方向の形または形式で定義したいと考えています。ここで重要なのは、関係がサブクラス化に対してブラインドであることです。私のポリモーフは、最初は個々のサブクラス タイプとしてではなく、ターゲットとしてのみ相互作用します。Task で各サブクラスを定義すると、ポリモーフィックな関係の目的が失われ始めます。
  • 結合テーブルのモデルが適切である可能性があることがわかりました。これは、Rails が喜んで廃止すると想定していた複雑さを追加することを除けば、ある程度正しいように思えます。これについては未経験でお願いします。

これは、Rails の機能またはコミュニティ全体の知識に小さな穴があるようです。うまくいけば、stackoverflow が私の答えの検索を記録できることを願っています!

助けてくれたみんなに感謝します!

4

7 に答える 7

58

ポリモーフィズムを組み合わせhas_many :throughて、柔軟なマッピングを取得できます。

class Assignment < ActiveRecord::Base
  belongs_to :task
  belongs_to :target, :polymorphic => true
end

class Task < ActiveRecord::Base
  has_many :targets, :through => :assignment
end

class Store < ActiveRecord::Base
  has_many :tasks, :through => :assignment, :as => :target
end

class Vehicle < ActiveRecord::Base
  has_many :tasks, :through => :assignment, :as => :target
end

...などなど。

于 2009-10-08T19:52:12.000 に答える
15

SFEley によって提案された回答は素晴らしいものですが、いくつかの欠点があります。

  • ターゲット (店舗/車両) からのタスクの取得は機能しますが、逆方向は機能しません。これは基本的に、SQL がどのテーブルにあるかを判断できないため、ポリモーフィック データ型への :through 関連付けをトラバースできないためです。
  • :through 関連付けを持つすべてのモデルには、中間テーブルとの直接関連付けが必要です
  • :through Assignment 関連付けは複数形にする必要があります
  • :as ステートメントは :through と一緒には機能しません。最初に、中間テーブルとの直接の関連付けを指定して指定する必要があります。

それを念頭に置いて、私の最も簡単な解決策は次のとおりです。

class Assignment < ActiveRecord::Base
  belongs_to :task
  belongs_to :target, :polymorphic => true
end

class Task < ActiveRecord::Base
  has_many :assignments
  # acts as the the 'has_many targets' needed
  def targets
    assignments.map {|x| x.target}
  end
end

class Store < ActiveRecord::Base
  has_many :assignments, as: :target
  has_many :tasks, :through => :assignment
end

class Vehicle < ActiveRecord::Base
  has_many :assignments, as: :target
  has_many :tasks, :through => :assignment, :as => :target
end

参考文献: http://blog.hasmanythrough.com/2006/4/3/polymorphic-through

于 2015-09-19T15:57:22.293 に答える
1

あなたが言及した has_many_polymorphs ソリューションはそれほど悪くはありません。

class Task < ActiveRecord::Base
  has_many_polymorphs :targets, :from => [:store, :software, :office, :vehicle]
end

あなたが望むすべてを行うようです。

次のメソッドを提供します。

タスクへ:

t = Task.first
t.targets   # Mixed collection of all targets associated with task t
t.stores    # Collection of stores associated with task t
t.softwares # same but for software
t.offices   # same but for office
t.vehicles  # same but for vehicles

ソフトウェア、ストア、オフィス、車両へ:

s = Software.first    # works for any of the subtargets.
s.tasks               # lists tasks associated with s

コメントを正しくフォローしている場合、残っている唯一の問題は、新しいタイプのサブターゲットを作成するたびに app/models/task.rb を変更する必要がないことです。Rails の方法では、双方向の関連付けを作成するために 2 つのファイルを変更する必要があるようです。has_many_polymorphs では、Tasks ファイルを変更するだけで済みます。私には勝利のように思えます。または、少なくとも、とにかく新しいモデル ファイルを編集する必要がなければ、そうするでしょう。

これを回避する方法はいくつかありますが、時々 1 つのファイルを変更するのを避けるには、あまりにも手間がかかりすぎるように思えます。しかし、Task を自分で変更してポリモーフィックな関係に追加することにまったく反対している場合は、次のように提案します。

サブターゲットのリストを保持します。lib/subtargets で、基本的に table_name.underscore である行ごとに 1 つのエントリをフォーマットすることをお勧めします。(大文字にはアンダースコアが前に付き、すべて小文字になります)

store
software
office
vehicle

config/initializers/subtargets.rb を作成し、次のように入力します。

SubtargetList = File.open("#{RAILS_ROOT}/lib/subtargets").read.split.reject(&:match(/#/)).map(&:to_sym)

次に、カスタム ジェネレーターまたは新しい rake タスクを作成します。新しいサブターゲットを生成し、上で定義したサブターゲット リスト ファイルにモデル名を追加するには。おそらく、変更を加えて引数を標準のジェネレーターに渡す必要最小限の作業を行うことになるでしょう。

申し訳ありませんが、今は詳しく説明する気がしませんが、リソースをいくつかご紹介します

最後に has_many_polymorphs 宣言のリストを SubtargetList に置き換えます

class Task < ActiveRecord::Base
  has_many_polymorphs :targets, :from => SubtargetList
end

この時点から、新しいサブターゲットを追加できます

$ script/generate subtarget_model home

これにより、コンソールをリロードするか本番サーバーを再起動すると、ポリモーフィック リストが自動的に更新されます。

前述したように、サブターゲット リストを自動的に更新するのは大変な作業です。ただし、このルートを使用する場合は、カスタム ジェネレーターを微調整して、生成時にサブターゲット モデルのすべての必要な部分がそこにあることを確認できます。

于 2009-10-11T07:42:30.443 に答える
1

STI の使用:

class Task < ActiveRecord::Base
end

class StoreTask < Task
  belongs_to :store, :foreign_key => "target_id"
end

class VehicleTask < Task
  belongs_to :vehicle, :foreign_key => "target_id"
end

class Store < ActiveRecord::Base
  has_many :tasks, :class_name => "StoreTask", :foreign_key => "target_id"
end

class Vehicle < ActiveRecord::Base
  has_many :tasks, :class_name => "VehicleTask", :foreign_key => "target_id"
end

あなたのデータベースには以下が必要です: Task type:stringそしてTask target_id:integer

利点は、特定のタスク タイプごとにスルー モデルがあることです。

STI とポリモーフィック モデルを合わせて参照してください

乾杯!

于 2012-01-12T22:11:38.910 に答える
0

そのブルートフォースアプローチを追求しましたか:

class Task 
  has_many :stores
  has_many :softwares
  has_many :offices
  has_many :vehicles

  def targets
    stores + softwares + offices + vehicles
  end
  ...

それほどエレガントではないかもしれませんが、正直なところ、それほど冗長ではなく、コードについて本質的に非効率的なものは何もありません。

于 2009-09-26T03:32:00.800 に答える
0

私は、STI と委任を組み合わせて使用​​するソリューションを採用する方がはるかに実装しやすいという意見に同意します。

問題の中心にあるのは、Target のすべてのサブクラスのレコードをどこに保存するかです。ActiveRecord は、STI モデルを介してデータベースを選択します。

それらをターゲットのクラス変数に格納し、継承されたコールバックを使用して新しいものを追加することができます。次に、その配列の内容から必要なコードを動的に生成し、method_missing を活用できます。

于 2009-08-22T08:41:47.433 に答える