你怎么写cancan能力呢?和accessible_by两者都在自我引用HABTM关系?

时间:2011-12-23 07:43:28

标签: ruby-on-rails cancan

我很难理解如何使用自引用HABTM关系,cancan和ActiveRecord来处理这种情况。

我正在尝试使用 accessible_by 来确定一组视频,这些视频在视频和频道之间存在关系时可见,但生成的SQL的部分查询表名称错误。以下是关系:

# video.rb
class Video < ActiveRecord::Base
  belongs_to :channel
end

# channel.rb
class Channel < ActiveRecord::Base
  has_many :videos
  has_and_belongs_to_many :subchannels, :class_name => "Channel", :join_table => "channels_channels", :foreign_key => :parent_id, :association_foreign_key => :subchannel_id
  has_and_belongs_to_many :parent_channels, :class_name => "Channel", :join_table => "channels_channels", :foreign_key => :subchannel_id, :association_foreign_key => :parent_id
end

# The appropriate channels_channels table exists with subchannel_id and parent_id fields.

我需要 cancan能力来查找默认频道的公共子频道中的所有公开视频。我尝试了以下方法:

# ability.rb
can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {:name => "Default"}}

在控制台中,当我尝试这个时,我得到以下内容:

> Video.accessible_by(Ability.new(nil))
SELECT "videos".* FROM "videos" INNER JOIN "channels" ON "channels"."id" = "videos"."channel_id" INNER JOIN "channels_channels" ON "channels_channels"."subchannel_id" = "channels"."id" INNER JOIN "channels" "parent_channels_channels" ON "parent_channels_channels"."id" = "channels_channels"."parent_id" WHERE "videos"."permission" = 'public' AND "channels"."permission" = 'public' AND "channels"."name" = 'Default'
=> [] 

我有一些应该会产生视频的记录,而且更重要的是,预计查询的结尾会有“parent_channels_channels”。“name”='Default',但表格是“channels”,导致错误的数据集,因为SQL引用了通道的名称,而不是父通道。

我尝试按如下方式更改功能以强制使用表名:

    can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {"parent_channels_channels.name" => "Default"}}

这会产生正确的查询,而 accessible_by 会返回正确的数据集,但可以?(:读取,视频)表示该结果集中的视频会导致运行时错误。

NoMethodError: undefined method `parent_channels_channels.name' for #<Channel:0x007fef35593748>

如何指定此功能,以便它与可以? accessible_by 一起使用?

1 个答案:

答案 0 :(得分:0)

我从您的代码中不清楚您是否正确建模。你真的希望parent_channels和subchannels成为HABTM关系,甚至是同一个模型吗?也许你的应用程序中的一些真实世界的例子会对我有帮助,但看起来你想要的是一个父视频类别,你想要与频道的HABTM关系,然后每个频道都与视频有一个很好的关系。

然后你可以让VideoCategory有一个has_many:videos,:through =&gt; :渠道关系。

class VideoCategory
  has_and_belongs_to_many :channels

  has_many :videos, :through => :channels
end

class Video
  belongs_to :channel
end

class Channel
  has_many :videos

  has_and_belongs_to_many :video_cateogories
end

看起来你正试图让Channel类不仅仅是一个频道。父母和孩子的概念在habtm关系中没有多大意义。通常,如果您需要父类和子类,则需要1对多关系。多对多意味着两个关联都不是父类(当然,这可能与您在UI中呈现它的方式不同)。

如果你真的必须这样做,那么你是否尝试过使用范围呢?

class Video
  scope :public, lambda do
    joins(:channel).where("videos.permission = 'public' and channels.permission = 'public'")
  end
end

似乎将逻辑放入范围并且没有能力.rb可能有助于您更好地控制SQL编写的内容 - 它删除了CanCan正在进行的抽象层。