在Rails 4中相当于has_many'conditions'选项是什么?


73

有人可以告诉我在Rails 4中执行以下行的等效方法是什么吗?

has_many :friends, :through => :friendships, :conditions => "status = 'accepted'", :order => :first_name

我尝试了以下方法:

has_many :friends, -> { where status: 'accepted' }, :through => :friendships , :order => :first_name

但是我收到以下错误:

Invalid mix of scope block and deprecated finder options on ActiveRecord association: User.has_many :friends

1
嗯...您可以发布整个文件吗?我以前从未见过该错误。
万花筒

嗯,没关系,我想我知道怎么了,会更新...
万花筒

Answers:


120

需要成为第二个arg:

class Customer < ActiveRecord::Base
  has_many :orders, -> { where processed: true }
end

http://edgeguides.rubyonrails.org/association_basics.html#scopes-for-has-many

响应更新:

将订单放入块内:

has_many :friends, -> { where(friendship: {status: 'accepted'}).order('first_name DESC') }, :through => :friendships

在第二个arg中替换它之后,出现以下错误:ActiveRecord关联上无效的作用域块和不建议使用的查找器选项组合:User.has_many:friends
medBouzid 2013年

您能用尝试过的内容更新您的问题吗?看来您的评论空间不足。
万花筒

我用了-> {其中状态:'requested',:order =>:created_at},它对我
有用

谢谢您的帮助,请添加我的解决方案作为第二个建议^^
medBouzid 2013年

@medBo我不确定您的解决方案是否按照您的想法进行。我认为最终要对进行评估where(status: 'requested', order: :created_at),而不是对关系实际应用顺序。您可以通过检查生成的SQL(a_customer.friends.to_sql)来验证这一点
Kaleidoscope 2013年

65

尽管此处的其他答案在技术上都是正确的,但它们违反了封装。该用户模型不应该知道的是,友谊模型有一个叫列status,并且它可以有这样一个特定的值accepted

例如,如果您决定进行更改,以利用Rails 4中的枚举,则必须同时更改用户友谊模型。这可能会导致维护封装避免的错误。

我将在“友谊”模型中公开范围:

scope :accepted, -> { where(status: :accepted) }

然后我会用这个范围的用户模式,隐藏任何实现细节的用户

has_many :friendships, -> { Friendship.accepted }
has_many :friends, through: :friendships

# Or...

has_many :friends, -> { Friendship.accepted }, through: :friendships

您可以更进一步,将范围重命名accepted_friendships为更清晰。

has_many :accepted_friendships, -> { Friendship.accepted }
has_many :friends, through: :accepted_friendships

现在,您已经成功地将实现细节封装在各自的模型中。万一发生任何更改,您只有一个地方可以更改它,从而减少了维护并提高了耐用性。


1
您的解决方案看起来更干净,我将在以后尝试。+1
medBouzid

我使用的是相同类型的代码,但使用以下代码syntax error, unexpected '\n', expecting =>has_many :friends, :through => :friendships, class_name: "User", -> { Friendship.accepted }
Riptyde4,2016年

我正在使用Rails 4.2.5,直到我将where放在其余参数之前,它才接受语法
Riptyde4 '16

@ Riptyde4对不起,我没有正确阅读您之前的评论:->{ Friendship.accepted }正如我的回答所示,proc()必须是第二个参数。所以是的,如果您不按该顺序进行操作,则将出现语法错误。我的答案以正确的顺序显示:has_many :friends, -> { Friendship.accepted }, through: :friendships
Mohamad

@Mohamad对不起,对于我的困惑,当我为自己尝试时,我没有意识到顺序很重要。您的回答对我非常有帮助
Riptyde4 '16

5

Mohamad的答案的Rails 3.2版本如下:

class Friend < ActiveRecord::Base
  has_many :friendships, :order => :first_name

  has_many :friends, :through => :friendships,
           :conditions => proc { Friendship.accepted.where_ast }

  has_many :pending_friends, :through => :friendships,
           class_name => Friend,
           :conditions => proc { Friendship.pending.where_ast }
end

class Friendship < ActiveRecord::Base
  scope :status, ->(status) { where(:status => status) }
  scope :accepted, -> { status('accepted') }
  scope :pending, -> { where(arel_table[:status].not_eq('accepted')) } 
end

笔记:

  • where_ast 这很重要,因为它会返回条件起作用所需的AREL节点
  • 在传递给的proc中:conditionsself它并不总是模型实例(例如,当关联与另一个查询合并时)
  • 在您的范围和关联中使用原始SQL可能会在某种程度上引起表名称命名空间的问题...使用AREL。

3

为了在Rails 4.1上工作(我的情况),我不得不提出:

has_many :friends, -> { where(friendships: { status: 'accepted' }) }, through: :friendships

注意友谊上的S。它直接引用数据库名称。


0
has_many :friends, -> { where(status: 'accepted').order('first_name')}, through: :friendships

要么

has_many :friends, -> { where(status: 'accepted').order(:first_name)}, through: :friendships
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.