ruby-on-rails – Rails关联和复杂形式 – 如何正确验证?
想象一下,你正在为教授们建立一个非常复杂的应用程序来为他们的学生提供考试.您可能会想象rails模型层可能如下所示:
class Exam < ActiveRecord::Base belongs_to :professor has_many :questions has_many :student_exams # exam_name: string # exam_start_time: string end class StudentExam < ActiveRecord::Base belongs_to :student belongs_to :exam end class Question < ActiveRecord::Base belongs_to :exam has_many :answers # question_text: text end class Answer < ActiveRecord::Base belongs_to :student_exam belongs_to :question has_many :gradings # answer_text: text end class Grading < ActiveRecord::Base belongs_to :answer belongs_to :grader # score: integer # grader_notes: text end 至少在这个问题上,忽略任何其他可能存在的模型(为什么不存在属于教授然后has_many的课程模型:学生?教授和学生模型是什么样的?). 这个想法是教授创建一个问题考试,将学生与考试联系起来(通过StudentExam),然后这些学生为每个问题创建一个独特的答案.然后每个答案由一个或多个评分者获得评分. 问题来自于我正在尝试制作评分表格 – 控制器将更新和创建的内容与表单一起显示?作为一名平地机,您可能希望整个考试布局,然后您可以在那里评估学生的答案.所以我认为表单可能看起来像: = simple_form_for @exam do |f| = f.object.exam_name = f.simple_fields_for :questions do |q| = q.object.question_text = q.simple_fields_for :answers do |a| = a.object.response_text = a.simple_fields_for :gradings do |g| = g.score = g.grader_notes (当然,这假定考试接受_sidted_attributes_for:questions,Question accepts_nested_attributes_for:answers,Answer accepts_nested_attributes_for:evaluation). 在我看来,这个形式的问题是我觉得它非常嵌套并且难以验证.想到的想法: >对于q.simple_fields_for:答案不仅仅是学生我正在评分的答案,并没有真正链接到考试,对吧? 在我提交的控制器方面,我如何验证作为平地机,我不是在修改另一个平地机的评分?或者,Grader没有恶意将评分与不同的答案联系在一起,而这些答案是他们可能无法访问的不同考试的一部分?创建/更新会是什么样的? strong_parameters会是什么样子?我的第一个猜测是这样的: class GradingController < ActionController::Base # .... create @exam = grader.exams.find(params[:id]) @exam.new(grading_params) end private def grading_params params(:exam).permit(:questions_attributes => [:answers_attributes => [:grading_attributes => [:score,:grader_notes]]) end end 但是现在我如何将评分者与评分相关联?我没有向grading_id添加:grader_id到grading_attributes的strong_params,因为那时他们可以使用与他们自己不同的grader_id提交参数. 同样地,我没有将:id列放在answers_attributes中,因为我觉得有人可能会恶意更改该ID并将其中一个评分与另一个考试中的不同答案相关联,尤其是因为答案仅与问题相关联用foreign_key,而不是回到考试(对吧?). 我能想到解决这个问题的唯一方法就是将提交的参数分开并一路检查它们,这看起来很疯狂. IE之类的东西: class GradingController < ActionController::Base # .... def create @exam = grader.exams.find(params[:id]) params[:exam][:question_attributes].each do |question_hash| question = @exam.questions.find(question_hash[:id]) if question.present? answer_hashes = question_hash[:answer_attributes] answer_hashes.each do |answer_hash| answer = question.answers.find(answer_hash[:id]) if answer.present? grading_hashes = answer_hash[:grading_attributes] grading_hashes.each do |grading_hash| grading = answer.gradings.where(grading_hash[:id]).first_or_initialize grading.grader = current_grader grading.score = grading_hash[:score] grading.grader_notes = grading_hash[:grader_notes] end end end end end end end 这看起来非常脆弱,也非常复杂,并且无法正确返回(如果答案或问题不存在,我们如何处理或显示表单上的错误等).但这里的另一个选择是什么? 解决方法
首先,你在上面描述的方法,如果不是很好的复杂情况,就像你已经发现的那样.使用accepts_nested_attributes并在其他字段中嵌套所有字段变得非常复杂,并且在我的意见中具有非常静态的特性(并且它也很难维护).
对于rails的简单的accepts_nested_attirbute函数而言,过于复杂的情况下,您可以使用表单对象来处理验证和保存多个模型.这种方法不需要这种模型嵌套,并且可以弥补表单所带来的复杂性. 基本上,你做的是移动一些,或者如果你创建一个表单类,它可以存储表单中的所有信息,并在验证成功后保存所有模型.与上面提到的博客文章相比,它使用virtus gem来创建虚拟字段,我个人使用ActiveData来完成这项工作,但你也可以坚持使用ActiveRecord模型并在那里保持验证并将错误转发给表单对象. 您的表单对象应该做的是: >获取表单的参数并从中实例化对象 您可以使用simple_forms,通过手动将参数设置为结构,您认为有意义,而您的表单对象可以使用这些结构. 我认为没有创建表单对象的一般接受,但我认为通过上面的信息,您应该有一个良好的开端,找出创建表单对象的最佳方法. 作为一个例子,我可以向你展示一个表单对象,我创建的购物车,没有额外的宝石,也可以通过将数据保存到会话中来处理多个步骤:Form Object,Step 1,Step 2,Step 3 希望这可以帮助. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |