我想做这样的事情:
Category
--------
- id
- name
Tag
--------
- id
- tag
Campaign
--------
- id
- name
- target (either a tag *or* a category)
这是一个多态关联的答案吗?我似乎无法弄清楚如何使用has_one:target,:as => :靶向
基本上,我希望Campaign.target设置为Tag或Category(或将来可能是另一个模型)。
答案 0 :(得分:77)
我认为您不需要has_one
关联,belongs_to
应该是您正在寻找的。 p>
在这种情况下,您需要在广告系列表格中添加target_id
和target_type
列,您可以通过t.references :target
调用在其中创建这些列(其中t
}是table
变量。
class Campaign < ActiveRecord::Base
belongs_to :target, :polymorphic => true
end
现在广告系列可以与Tag
或Category
相关联,@campaign.target
会返回相应的广告系列。
如果目标表上的外键指向您的has_one
,则会使用Campaign
关联。
例如,您的表格会有
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不知道如何预先解决这个问题,因为tag
上的名称Campaign
没有关联。
可能会引起进一步混淆的另外两个选项是source
和source_type
选项。这些仅用于:through
关系,您实际上正在加入关联through
另一个表。文档可能更好地描述它,但source
定义了关联名称,source_type
用于该关联是多态的。只有当目标关联(在:through
类)上的名称不明显时才需要使用它们 - 就像上面target and
标记的情况一样 - 我们需要告诉rails一个人可以使用。
答案 1 :(得分:5)
这些问题的答案很棒,但我只是想提到另一种方法来实现同样的目标。你可以做的是创建两个关系,例如:
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查询。
答案 2 :(得分:1)
轻微的附录:在您创建Campaign
表的迁移中,t.references :target
调用应该:polymorphic => true
(至少使用rails 4.2)