如果不存在Rails关联集合构建

时间:2014-11-07 12:58:15

标签: ruby-on-rails

我有三个模型ExamUserExamResultExamResult包含所有考试(考试)学生(用户)的记录。对于一个特定的Exam记录,每个学生在ExamResult中应该有一条记录。在edit的{​​{1}}方法中,根据是否为一个学生创建了ExamController记录,我需要构建一个新记录或者只是跳过它。不确定这是否是惯用的Rails方式。

ExamResult

或者这样:

# ExamController

def edit
  User.students.each do |s|
    @exam.exam_results.build(user_id: s.id) unless @exam.exam_results.find_by(user_id: s.id)
  end   
end

也许两者都不是惯用的Rails。欢迎任何建议。

修改

在桌面上带上def edit newIds = User.students.map(&:id) - @exam.exam_results.map(&:user_id) newIds.each do |id| @exam.exam_results.build(user_id: id) end end (由@ user3334690推荐)。如果我理解正确的文档,这应该与前两个实现相同。

find_or_initialize_by

3 个答案:

答案 0 :(得分:4)

在上述情况下,您可以使用find_or_create_by_user_id代替构建。

def edit
  User.students.each do |s|
    ExamResult.find_or_create_by_exam_id_and_user_id(exam_id, user_id)
  end   
end

答案 1 :(得分:3)

就是这样:

def edit
    User.students.each do |s|
       ExamResult.where(exam_id: @exam.id, user_id: s.id).first_or_create
    end   
end

答案 2 :(得分:0)

使用你的第二个例子:

def edit
  newIds = User.students.map(&:id) - @exam.exam_results.map(&:user_id)
  newIds.each do |id|
    @exam.exam_results.build(user_id: id)
  end
end

正如我在上面的评论中所说,这将对数据库进行两次查询,而不管学生数量会更好。如果您处于一个非常极端的环境中,您可以将其编写为只使用一个查询以及"采取"只拉动" id"列(并避免对象创建开销),如下所示:

newIds = User.students.where("users.id not in (select user_id from exam_results where exam_id=?)", @exam.id).pluck(:id)

然而,可读性因此而降低。你的原版也可以使用" pluck"而不是" map"。

另一种风格说明 - 我会使用" new_ids"这是使用Rails执行此操作的标准惯用方法。