我正在建立一个与武术相关的数据库,目前我已经建立了以下协会:
我根据学生的风格生成如下表格:
所以标题是由Style
模型(太极,空手道......)生成的,然后是下面列出的排名(取自Rank
模型),以及“Dojo”和“一旦创建,日期“字段应属于Grading
模型。
问题:我知道如何构建一个创建一个关联(或一个关联及其子项)的表单,但是如何构建一次创建多个关联的表单?
此外,实现以下内容的干净方法是什么:
这是我目前用于检索正确记录的内容:
class GradingsController < ApplicationController
before_filter :authenticate_sensei!
def index
@student = Student.includes(:styles).find(params[:student_id])
@ranks = Rank.for_student_styles(@student)
split_ranks_by_style
end
private
def split_ranks_by_style
@karate = @ranks.select_style("Karate")
@tai_chi = @ranks.select_style("Tai Chi")
@weaponry = @ranks.select_style("Weaponry")
end
end
# Rank model
def self.for_student_styles(student)
includes(:style).where("styles.id in (?)", student.styles.map(&:id))
end
def self.select_style(style)
all.map { |r| r if r.style.name == style }.compact
end
答案 0 :(得分:1)
这样的复杂表单最好在主要资源的create
或update
操作中启动的服务对象中处理。这使您可以轻松找到之后逻辑发生的位置。在这种情况下,您似乎可以在GradingsController
中启动服务对象。我也更喜欢格式化标记中的大量数据,以便在服务对象中更容易处理。这可以通过传递"grade[style]"
和"grade[rank]"
之类的名称来完成。这将格式化您的params作为方便的哈希:{grade: {style: "karate", rank: "3"}}
。该哈希值可以传递给您的服务对象以进行解析。
如果没有真正掌握您的具体要求的全部范围,请将示例表格放在一起:
<%= form_for :grading, url: gradings_path do |f| %>
<h1><%= @rank.name %></h1>
<%- @grades.each do |grade| %>
<div>
<%= hidden_field_tag "grade[#{grade.id}][id]", grade.id %>
<%= check_box_tag "grade[#{grade.id}][active]" %>
...
<%= text_field_tag "grade[#{grade.id}][date]" %>
</div>
<%- end %>
<%= submit_tag %>
<%- end %>
使用这样的表格,你可以让你的参数进入控制器,看起来像这样:
"grade"=>{
"1"=>{"id"=>"1", "active"=>"1", "date"=>"2013-06-21"},
"3"=>{"id"=>"3", "date"=>"2013-07-01"}
}
很好地格式化我们交给我们的服务对象。让我们的控制器保持良好和干净:
class GradingsController < ApplicationController
def index
# ...
end
def create
builder = GradeBuilder.new(current_user, params['grade'])
if builder.run
redirect_to gradings_path
else
flash[:error] = 'Something went wrong!' # maybe even builder.error_message
render :action => :index
end
end
end
所以现在我们只需要将任何自定义逻辑放入我们的构建器中,我可能建议在/lib
目录中创建一个简单的ruby类。它可能看起来像这样:
class GradeBuilder
attr_reader :data, :user
def self.initialize(user, params={})
@user = user
@data = params.values.select{|param| param['active'].present? }
end
def run
grades = data.each{|entry| build_grade(entry)}
return false if grades.empty?
end
private
def build_grade(entry)
grade = Grade.find(entry['id'])
rank = grade.rankings.create(student_id: user, date: entry['date'])
end
end
显然需要更多的工作来传递表单中所需的所有特定数据,以及GradeBuilder
中用于处理边缘情况的额外逻辑,但这将为您提供一个框架来处理此问题一种可维护和可扩展的方式。