如果不存在,则生成Rails关联集合



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

# 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

或者这样:

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

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

编辑

find_or_initialize_by(由@user3334690推荐)放在桌子上。如果我正确地理解了文档,这应该与前两个实现相同。

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

在上述情况下,您可以使用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

有这样的方法:

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

使用第二个示例:

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

正如我在上面的评论中所说,无论学生人数多少,这都会对数据库进行两次查询,这样会更好地扩展。如果你在一个非常极端的环境中,你可以写它只使用一个查询,同时使用"pull"只提取"id"列(并避免对象创建开销),如下所示:

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

然而,可读性因此而降低。你的原作也将受益于使用"勇气"而不是"地图"。

另一个风格注意事项是,我将使用"new_ids",这是使用Rails进行操作的标准惯用方式。

最新更新