可能在rails中有"polymorphic has_one"关系?

mar*_*ada 37 polymorphism activerecord ruby-on-rails

我想做这样的事情:

Category
--------
- id
- name

Tag
--------
- id
- tag


Campaign
--------
- id
- name
- target (either a tag *or* a category)
Run Code Online (Sandbox Code Playgroud)

这是一个多态关联的答案吗?我似乎无法弄清楚如何使用has_one:target,:as =>:targetable.

基本上,我希望Campaign.target设置为Tag或Category(或将来可能是另一个模型).

Kri*_* PD 78

我不相信你在has_one这里需要一个协会,belongs_to应该是你正在寻找的.

在这种情况下,您需要在Campaign表上添加一个target_idtarget_type列,您可以通过t.references :target调用在rake中创建这些列(其中ttable变量).

class Campaign < ActiveRecord::Base
  belongs_to :target, :polymorphic => true
end
Run Code Online (Sandbox Code Playgroud)

现在,运动可以关联到任何一个TagCategory并且@campaign.target将返回相应的一个.

has_one如果目标表上的外键指向您的外键,则将使用该关联Campaign.

例如,你的表会有

Tag: id, tag, campaign_id Category: id, category, campaign_id

并且会对belongs_to :campaign两者都有关联.在这种情况下,您必须使用has_one :taghas_one :category,但此时您无法使用通用target.

那更有意义吗?

编辑

由于target_id并且target_type实际上是另一个表的外键,因此您Campaign属于其中一个表.我可以看到你对措辞的困惑,因为逻辑上它Campaign是容器.我猜你可以把它想象成Campaign一个目标,那是一个Tag或一个Container,因此它属于一个Tag或者Container.

has_one是说在目标类上定义关系的方式.例如,a Tag将通过has_one关系与广告系列相关联,因为标记类上没有任何标识关联的内容.在这种情况下,你有

class Tag < ActiveRecord::Base
  has_one :campaign, :as => :target
end
Run Code Online (Sandbox Code Playgroud)

同样的Category.在这里,:as关键字告诉rails哪个关联与此有关Tag.Rails不知道怎么算出来的前期,因为有这个名字没有关联tagCampaign.

可提供进一步的混乱另外两个选项是sourcesource_type选项.这些仅用于:through您实际加入through另一个表的关系的关系中.文档可能更好地描述它,但是source定义关联名称,并且source_type在关联是多态的地方使用.它们只需要在目标关联(在:through类上)具有不明显的名称时使用 - 就像上面的target andTag一样 - 我们需要告诉rails使用哪一个.

  • 我添加了一些注释,试图按照我的方式帮助描述它,希望有所帮助. (7认同)
  • 非常有帮助.希望我能给你更多积分.谢谢! (3认同)

tro*_*skn 7

这些问题的答案很棒,但我只是想提一下另一种方法来实现同样的目标.你可以做的是创建两个关系,例如:

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

验证确保您不会意外地最终设置两个字段,并且target帮助程序允许对标记/类别进行多态访问.

这样做的好处是,您可以获得更正确的数据库模式,您可以在其中为id列定义正确的外键约束.这也将在数据库级别上导致更好,更有效的SQL查询.