私はこのようなことをしたいと思います:
Category
--------
- id
- name
Tag
--------
- id
- tag
Campaign
--------
- id
- name
- target (either a tag *or* a category)
多態的な関連はここでの答えですか? has_one:target、:as =>:targetableでそれを使用する方法を理解できないようです。
基本的には、Campaign.targetをタグまたはカテゴリ(または将来的には別のモデルになる可能性がある)に設定する必要があります。
ここでhas_one
の関連付けが必要だとは思わないので、belongs_to
を探してください。
この場合、キャンペーンテーブルにtarget_id
列とtarget_type
列が必要です。t.references :target
呼び出し(t
はtable
変数)を使用して、これらをレーキで作成できます。
class Campaign < ActiveRecord::Base
belongs_to :target, :polymorphic => true
end
これで、キャンペーンをTag
またはCategory
のいずれかに関連付けることができ、@campaign.target
は適切なものを返します。
Campaign
を指す外部キーがターゲットテーブルにある場合、has_one
関連付けが使用されます。
たとえば、テーブルには
Tag: id, tag, campaign_id
Category: id, category, campaign_id
両方にbelongs_to :campaign
関連付けがあります。この場合、has_one :tag
とhas_one :category
を使用する必要がありますが、この時点では一般的なtarget
を使用できませんでした。
それはもっと理にかなっていますか?
[〜#〜]編集[〜#〜]
target_id
とtarget_type
は事実上別のテーブルへの外部キーであるため、Campaign
はそれらのいずれかに属しています。論理的にはCampaign
がコンテナであるため、この文言の混乱を理解できます。 Campaign
には単一のターゲットがあり、Tag
またはContainer
であるため、Tag
またはContainer
に属していると考えることができます。
has_one
は、関係がターゲットクラスで定義されていることを示す方法です。たとえば、タグクラスには関連付けを識別するものがないため、Tag
はhas_one
関係を介してキャンペーンに関連付けられます。この場合、
class Tag < ActiveRecord::Base
has_one :campaign, :as => :target
end
同様に、Category
の場合も同様です。ここで、:as
キーワードは、どの関連付けがこのTag
に関連しているかをRailsに伝えています。 Railsは、tag
にCampaign
という名前との関連付けがないため、これを前もって把握する方法を知りません。
さらに混乱を招く可能性のある他の2つのオプションは、source
およびsource_type
オプションです。これらは:through
リレーションシップでのみ使用され、ここで実際にthrough
関連付けを別のテーブルに結合します。ドキュメントはおそらくそれをよりよく説明しますが、source
は関連名を定義し、source_type
はその関連が多態性である場合に使用されます。これらは、ターゲットの関連付け(:through
クラス上)に、上記のtarget and
Tagの場合のように明確ではない名前があり、Railsどちらを使用するか。
この質問への回答は素晴らしいですが、同じことを達成する別の方法についてお話ししたいと思います。代わりにできることは、2つの関係を作成することです。
class Campaign < ActiveRecord::Base
belongs_to :tag
belongs_to :category
validate :tag_and_category_mutually_exclusive
def target=(tag_or_category)
case
when tag_or_category.kind_of?(Tag)
self.tag = tag_or_category
self.category = nil
when tag_or_category.kind_of?(Category)
self.category = tag_or_category
self.tag = nil
else
raise ArgumentError, "Expected Tag or Category"
end
end
def target(tag_or_category)
tag || category
end
private
def tag_and_category_mutually_exclusive
if tag && category
errors.add "Can't have both a tag and a category"
end
end
end
検証により、誤って両方のフィールドが設定されてしまうことがなくなり、target
ヘルパーにより、タグ/カテゴリへの多態的なアクセスが許可されます。
このようにすることの利点は、ID列に適切な外部キー制約を定義できる、やや正確なデータベーススキーマが得られることです。これにより、データベースレベルでのより適切で効率的なSQLクエリも可能になります。
わずかな補遺:Campaign
テーブルを作成した移行では、t.references :target
呼び出しには:polymorphic => true
(少なくともRails 4.2)