问题
Thanks to Ruby on Rails: How to gather values for child tables from a form? and "Agile Web Dev", I know how to have multiple models in a form using fields_for
. But I'm tearing my hair out over this one.
Suppose I have a model Person
. Person
has a name
attribute, and has_many :foos
. The Foo
model, in turn, has a colour
attribute.
Furthermore, I know that each Person
has precisely three Foos
. What should my Models, the new
and create
actions in PersonController
, and the new
view look like in order to present three nicely-labelled text-entry boxes, one for each Foo and capable of reporting validation errors, to allow my "new person" form to create the whole set of four objects in one go?
Also, can I do this without accepts_nested_attributes_for
?
回答1:
After some playing about with varied locations for square braces and different for loops, I think I've solved this. Here's what my code looks like now (with routes set up as per scaffolding, so that posting from /new
triggers create
).
models/person.rb
class Person < ActiveRecord::Base
has_many :foos
validates_presence_of :name
end
models/foo.rb
class Foo < ActiveRecord::Base
belongs_to :person
validates_presence_of :colour
validates_uniqueness_of :colour, :scope => "person_id"
end
controllers/people_controller.rb
def new
# Set up a Person with 3 defaulted Foos
@person = Person.new
(1..3).each { |i| @person.foos.build }
end
def create
# Create (but don't save) a Person as specified
@person = Person.new(params[:person])
# Create (but don't save) a Foo for each set of Foo details
@foos = []
params[:foo].each do |foo_param|
@foos << Foo.new(foo_param)
end
# Save everything in a transaction
Person.transaction do
@person.save!
@foos.each do |foo|
foo.person = @person
foo.save!
end
end
redirect_to :action => 'show', :id => @person
rescue ActiveRecord::RecordInvalid => e
@foos.each do |foo|
foo.valid?
end
render :action => 'new'
end
views/people/new.html.erb
<% form_for :person do |f| %>
<%= error_messages_for :object => [@person] + @person.foos %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<table>
<% @person.foos.each_with_index do |foo, index| @foo = foo%>
<tr>
<td><%= label :colour, "Foo colour #{index + 1}: " %></td>
<td><%= text_field("foo[]", "colour" %></td>
</tr>
<% end %>
</table>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
This seems to do the trick.
来源:https://stackoverflow.com/questions/2110857/creating-a-set-number-of-child-objects-from-a-parents-form