【问题标题】:Rails: Conditionally loading associationsRails:有条件地加载关联
【发布时间】:2011-03-26 20:24:26
【问题描述】:

我有模型clubcourse,其中一个课程属于一个俱乐部,而一个俱乐部有很多课程。

当我加载一个俱乐部时,我也想加载它的相关课程,但我只想加载那些符合条件测试的课程 (approved? == true)。

如果我直接参与课程,如何做到这一点很简单:

@courses = Course.find( :all, :conditions => {:approved => true } )

但我想将其作为声明的一部分:

@club = Club.find(params[:id])

因为我的观点是这样构建的,我宁愿不必改变所有观点。

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord associations


    【解决方案1】:

    如果你只考虑俱乐部的课程,如果它已经获得批准,你可以这样做

    class Club < ActiveRecord::Base
      has_many :courses, :conditions => {:approved => true}
    end
    

    在你的控制器中

    @club = Club.find(params[:id], :include => :courses)
    

    现在,我不知道我是否误解了你,但你说“你的观点是这样建立的”。你是说你的控制器吗?因为如果你的观点有这样的逻辑...... DHH 每次有人这样做都会杀死一只小猫。

    【讨论】:

    • 可怜的小猫,但没有。我害怕必须同时加载@club@courses 变量...这意味着在我的观点中摆脱所有@club.courses 引用。我有一个管理员命名空间,我想在其中加载未经批准的课程(好像条件不在模型中),我将如何使用您的解决方案来做到这一点?
    【解决方案2】:

    您可以为此使用default_scope

    class Club < ActiveRecord::Base
      has_many :courses, conditions => {:approved => true}
      default_scope :include => :courses
    end
    
    class Course < ActiveRecord::Base
      default_scope :conditions => {:approved => true}
    end
    

    现在你可以这样做了:

    @club = Club.find(1) # this will eager load approved courses.
    

    参考:

    Article 关于default_scope

    注 1

    我更改了Club 类中的courses 关联以选择批准的课程。理论上,这不是必需的,因为 Course 类具有默认范围。但是,看起来默认范围不适用于急切加载的查询。

    注2

    我个人不会急于通过default_scope 加载Course 对象。通过 default_scope 执行此操作可为您提供所需的不显眼的解决方案。

    我会将include 子句添加到find 调用中,以便仅在需要时急切加载Course 对象。

    注3

    @瑞安比格:

    Ryan Bates 在他的screen cast 中谈到了默认范围。他给出了一个使用默认范围排除已删除记录的示例,即

    default_scope :conditions => "delete_at IS NULL"
    

    我认为这个用例是相似的。正如我认为的问题,课程模型的主要操作是在批准的记录上,default_scopeconditions 选项确保了这一点。要覆盖 default_scope,用户可以使用with_exclusive_scope 方法。

    Club.with_exclusive_scope{find(1)}
    

    【讨论】:

    • 我不会在 default_scope 中使用 :conditions ever:Rails 如何区分您的 default_scope 条件和您指定的另一组条件?我会改为使用named_scope 并调用它。 @Chubas 的解决方案也不错。
    • 我不同意,过滤 deletedapproved 记录是 default_scope 的合适用法。在这种情况下,Course 模型上的每个find 操作都需要过滤未批准的课程。如果用户需要包含未批准的记录,他可以使用with_exclusive_scope
    • 用户说:because my views are built that way and I would rather not have to change all of them.,这个解决方案可以让用户不显眼地实现功能。
    • 我已经更新了我的答案以包含来自 Ryan Bates 的示例,其中:conditions 选项用于default_scope
    • 这看起来是最接近的解决方案。在控制器中加载模型时,有什么方法可以声明要使用的命名范围?
    【解决方案3】:

    最终的解决方案来自多种答案,但都没有完全解决。

    当前解决方案: 我利用course 模型中的几个命名范围来实现我想要的功能,同时保持我的视图尽可能通用(必须能够干涸代码)。

    所以course 模型看起来有点像这样:

    class Course < ActiveRecord::Base
      belongs_to :club
    
      named_scope :have_approval, :conditions => { :approved => true }
      named_scope :need_approval, :conditions => { :approved => false }
    end
    

    要收集所有批准的课程,很简单:

    @approved_courses = Course.have_approval
    

    或者当与club 合作时,获得俱乐部认可的课程就像:

    @club = Club.find(:first)
    @approved_courses_in_club = @club.courses.have_approval
    

    命名范围是人!

    【讨论】:

    • 您可以使用动态范围而不是named_scope,即@club.courses.scoped_by_approval(true)@club.courses.scoped_by_approval(false)
    猜你喜欢
    • 2018-01-24
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多