如何在Rails中创建“双方”多对多关系?

时间:2010-02-14 16:10:04

标签: ruby-on-rails database-design has-many belongs-to

假设我们有一个摄影网站。任何作者都可以订阅以接收来自任何其他作者的更新。显然,如果作者A订阅作者B并不意味着B订阅了A.所以我们建立模型

class Author < ActiveRecord::Base
  has_many :subscriptions
  has_many :subscribed_by_author, :through => :subscriptions, :source => :subscribed_to
end

class Subscription < ActiveRecord::Base
  belongs_to :author
  belongs_to :subscribed_to, :class_name => "Author", :foreign_key => "subscribed_to"
end

这样我们可以使用

  1. some_author.subscribed_by_author - some_author订阅的作者列表。
  2. 对于任何订阅,我们都可以知道两端(谁订阅了谁)
  3. 但问题是如何获得仅使用rails(不使用普通SQL)订阅某些作者的人员列表,即得到答案:“谁订阅了some_author?”

    问题:Rails中是否有任何能力使双方的关系工作,即不仅要写some_author.subscribed_BY_author而且要some_author_subscribed_TO_author?如果有,那么它是什么?

    P.S。显而易见的解决方案是

    1. 更改数据库设计,添加名为“direction”的列
    2. 每次创建订阅时创建2条记录
    3. 添加到作者模型

      has_many:subscribed_BY_author,:through =&gt; :subscriptions,:source =&gt; :subscribed_to,:conditions =&gt; “direction ='by'”

      has_many:subscribed_TO_author,:through =&gt; :subscriptions,:source =&gt; :subscribed_to,:conditions =&gt; “direction ='to'”

    4. 但我想知道是否有一个解决方案而不改变数据库设计。

2 个答案:

答案 0 :(得分:2)

我会使用简单的HABTM来做这样简单的事情,但无论如何你都需要一个连接表。

create_table :subscriptions do |t|
  t.column :author_id, :integer
  t.column :subscriber_id, :integer
end

点作者:

class Author < ActiveRecord::Base
  has_and_belongs_to_many :subscribers
    :class_name => "Author",
    :join_table => "subscriptions",
    :association_foreign_key => "subscriber_id"

  def subscriptions # "subscribers" is already included above
    self.subscribers.find(:all, :subscriber_id=>author.id) # hopefully not too 
  end                                                      # much SQL
end

如果您真的致力于您的方法名称:

  def subscribed_to_author
    subscribers
  end

  def subscribed_by_author(author)
    self.subscribers.find(:all, :subscriber_id=>author.id)
  end

创建一些连接(我将使SubscriptionsController成为RESTy)

SubscriptionsController < ApplicationController
  def create
    @author = Author.find(params[:author_id] # author to be subscribed to
    @user = current_user # user clicking the "subscribe" button

    @author.subscribers << @user # assuming authors should only 
    @author.save                 # be able to subscribe themselves
  end
end

显示名称或其他

@author.subscribers.each do |s|
  s.name
end
# ...or...and...also...
<%= render :partial => @author.subscribers -%>
<%= render :partial => @author.subscriptions -%>

答案 1 :(得分:0)

# Author model
has_many :subscriptions_to, :class_name => "Subscription", :foreign_key => "subscribed_to"
has_many :subscribed_to_author, :through => :subscriptions_to, :source => :author

据我所知 - 它有效! :)