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

Tro*_*roy 5 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.
Run Code Online (Sandbox Code Playgroud)

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

# ability.rb
can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {:name => "Default"}}
Run Code Online (Sandbox Code Playgroud)

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

> 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'
=> [] 
Run Code Online (Sandbox Code Playgroud)

我有一些应该产生视频的记录,而且更重要的是,预计查询结束时会有"parent_channels_channels"."name"="Default",但表格是"channel" in替换,导致错误的数据集,因为SQL引用了通道的名称,而不是父通道.

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

    can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {"parent_channels_channels.name" => "Default"}}
Run Code Online (Sandbox Code Playgroud)

这导致了正确的查询,并且accessible_by返回正确的数据集,但是对于该结果集中的视频可以?(:read,video)会导致运行时错误.

NoMethodError: undefined method `parent_channels_channels.name' for #<Channel:0x007fef35593748>
Run Code Online (Sandbox Code Playgroud)

如何指定此功能以便它可以同时使用accessible_by

Pat*_*kEm 0

从您的代码中我不清楚您是否正确建模。你真的希望parent_channels和subchannels是HABTM关系,甚至是同一个模型吗?也许您的应用程序中的一些真实示例会对我有所帮助,但您似乎想要的是父 VideoCategory,并且您希望 HABTM 与频道之间的关系,然后每个频道都与视频有 has_many 关系。

然后,您可以让 VideoCategory 具有 has_many :videos, :through => :channels 关系。

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

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

如果您确实必须这样做作为自我引用,您是否尝试过使用范围?

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

看来,将逻辑放入作用域并置于能力之外可能会帮助您更好地控制所编写的 SQL 内容——它消除了 CanCan 正在做的抽象层。