6

我有一个用户以前的“投票”的 ActiveRecord 关系......

@previous_votes = current_user.votes

我需要将这些过滤到仅针对当前“挑战”的投票,所以 Ruby 的select方法似乎是做到这一点的最佳方式......

@previous_votes = current_user.votes.select { |v| v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id }

但是我还需要更新这些记录的属性,并且该select方法将我的关系变成了一个无法更新或保存的数组!

@previous_votes.update_all :ignore => false
# ...
# undefined method `update_all' for #<Array:0x007fed7949a0c0>

如何像 select 方法一样过滤我的关系,但又不会失去使用 ActiveRecord 更新/保存项目的能力?

在谷歌周围寻找似乎named_scope' 出现在类似问题的所有答案中,但我无法弄清楚他们可以具体完成我所追求的。

4

4 回答 4

2

我相信您可以执行以下操作:

@entry = Entry.find(params[:entry_id])    
@previous_votes = Vote.joins(:entry).where(entries: { id: @entry.id, challenge_id: @entry.challenge_id })
于 2013-04-19T07:03:32.110 回答
2

一个很好的方法是使用scopes。在您的情况下,您可以按如下方式设置范围:

class Vote < ActiveRecord::Base
  scope :for_challenge, lambda do |challenge_id|
    joins(:entry).where("entry.challenge_id = ?", challenge_id)
  end
end

然后,您获取当前投票的代码将如下所示:

challenge_id = Entry.find(params[:entry_id]).challenge_id
@previous_votes = current_user.votes.for_challenge(challenge_id)
于 2013-04-22T19:57:18.447 回答
2

问题在于这select不是 SQL 方法。它获取所有记录并在 Ruby 端过滤它们。这是一个简化的示例:

votes = Vote.scoped
votes.select{ |v| v.active? }
# SQL: select * from votes
# Ruby: all.select{ |v| v.active? }

由于 update_all 是一种 SQL 方法,因此您不能在 Ruby 数组上使用它。您可以坚持在 Ruby 中执行所有操作,或者将其中的一些(全部)操作移到 SQL 中。

votes = Vote.scoped
votes.select{ |v| v.active? }
# N SQL operations (N - number of votes)
votes.each{ |vote| vote.update_attribute :ignore, false }
# or in 1 SQL operation
Vote.where(id: votes.map(&:id)).update_all(ignore: false)

如果您实际上不使用获取的投票,那么在 SQL 端执行整个选择和更新会更快:

Vote.where(active: true).update_all(ignore: false)

虽然前面的示例适用于您select的 . 如果您在 Rails 模型中设置了所有关系,您可以大致如下进行:

entry = Entry.find(params[:entry_id])
current_user.votes.joins(:challenges).merge(entry.challenge.votes)
# requires following associations:
# Challenge.has_many :votes
# User.has_many :votes
# Vote.has_many :challenges

Rails 将为您构建适当的 SQL。但是,如果某些东西不起作用,您总是可以退回到手动编写 SQL。

于 2013-04-22T20:41:20.767 回答
1

使用collection_select而不是select. 专门构建在返回对象collection_select之上,而不是像你得到的字符串数组。selectActiveRecordselect

@previous_votes = current_user.votes.collection_select { |v| v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id }

这应该@previous_votes作为对象数组返回

编辑:用另一种建议的方式更新这篇文章以在数组中返回这些 AR 对象

@previous_votes = current_user.votes.collect {|v| records.detect { v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id}}
于 2013-04-19T07:10:26.273 回答