Re: Editing Multiple Models in One Form

Cool idea of using negative numbers for the index, I'll have to try that sometime.

Railscasts - Free Ruby on Rails Screencasts

Re: Editing Multiple Models in One Form

Can you explain how you would put the form into a partial? I am having problems with the section

<% for @task in @project.tasks %>
  <%= error_messages_for :task %>
  <% fields_for "task[]" do |f| %>
    <p><%= f.text_field :name %></p>
  <% end %>
<% end %>

because the variables are unknown to a partial and initializing them when calling the partial does not seem to work.

Great tutorial!

Thanks,
Dennis

Re: Editing Multiple Models in One Form

Which part specifically are you attempting to move into a partial? The instance variables (@task or @project) should be accessible from the partial (I think). Are you not able to access them?

You could pass variables to the partial using the :locals parameter, however, some of these methods (such as error_messages_for) rely on the use of an instance variable instead of a local variable so it makes things a little more difficult. It's hard to say the best approach without knowing which part of the form you would like in the partial.

Railscasts - Free Ruby on Rails Screencasts

Re: Editing Multiple Models in One Form

Anyone know the best way to combine the creating and editing multiple models into one form partial? I'm looking to be able to add multiple models in the form for the create action, then edit and/or add multiple models for the update action. Any help would be greatly appreciated.

Re: Editing Multiple Models in One Form

I am using acts_as_attachment to upload 4 images and everything works great on that side. But I also want to be able to edit the form and the images. I have went through the tutorial but can not seem to get this to work. Does anyone know how I can go about doing this?
Thanks,

Re: Editing Multiple Models in One Form

This was a helpful tutorial. However, when I ran my version of your code, I noticed in the development log that each part of the update query ran inside its own transaction. So the parent record got updated in one transaction and then each child record was updated inside its own transaction. What's the proper way to put the whole group into a single transaction?

Re: Editing Multiple Models in One Form

Re: Transactions

From playing around with the code, this apparently will wrap everything into one transaction:

Project.transaction do
  @project.save!
  @project.tasks.each(&:save!)
end

I wasn't sure this would work as the tasks are a different model, but looking at the development log, it is all put into one BEGIN/COMMIT block.

Last edited by boone (2007-04-29 18:30:59)

Re: Editing Multiple Models in One Form

Hello there,

Thank you for this great tutorial. It really helped me.

However I have a problem and I was wondering if you could help me.
I have 3 models. Scholar, Professor and Researcher. Scholar is related to the other models with many-to-many relationship and I want to update them all in one form. I'm a totally newbie and I don't know if I have followed the right way. The update function that I have built is:

def update
    @scholar = scholar.find(params[:id])
    @scholar.attributes = params[:scholar]
   
    @scholar.researchers.each { |t| t.attributes = params[:researcher][t.id.to_s] }
    @scholar.professors.each { |p| p.attributes = params[:professor][p.id.to_s] }

    @scholar.researchers.each(&:valid?)
    @scholar.professors.each(&:valid?)
    if @scholar.valid? && @scholar.researchers.all? { |t| t.errors.empty? } && @scholar.professors.all? { |p| p.errors.empty? }
    @scholar.save!
    @scholar.researchers.each(&:save!)
    @scholar.professors.each(&:save!)
    redirect_to :action => 'show', :id => @scholar
    else
    render :action => 'edit'
    end
  end


The relevant abstract of edit.rhtml is:

<% form_tag :action => 'update', :id => @scholar do %>
   <%= render :partial => 'form1' %>
<% for @researcher in @scholar.researchers %>
   <%= error_messages_for :researcher %>
   <% fields_for "researcher[]" do |f| %>
    <tr><td><br/><br/></td></tr>
    <tr>
    <td><div align="right">?????</div></td>
    <td><div align="left"><%= f.text_field :name %></div></td>
    </tr>
    <tr>
    <td><div align="right">???????</div></td>
    <td><div align="left"><%= f.text_field :surname %></div></td>
          </tr>
    <tr>
    <td><div align="right">???????</div></td>
    <td><div align="left"><%= f.text_field :level %></div></td>
    </tr>
    <tr>
    <td><div align="right">??????????</div></td>
    <td><div align="left"><%= f.text_field :organisation %></div></td></tr>
    <tr>
    <td><div align="right">????????</div></td>
    <td><div align="left"><%= f.text_field :phone %></div></td>
    </tr>
    <tr>
    <td><div align="right">e-mail</div></td>
    <td><div align="left"><%= f.text_field :email %></div></td>
    </tr>   
   
   <% end %>
<% end %>

<% for @professor in @scholar.professors %>
    <%= error_messages_for :professor %>
    <% fields_for "professor[]" do |f| %>
    tr><td><br/><br/></td></tr>
    <tr>
    <td><div align="right">?????</div></td>
    <td><div align="left"><%= f.text_field :name %></div></td>
    </tr>
    <% end %>
<% end %>

<%= render :partial => 'form3' %>
 
  <%= submit_tag 'Edit' %>


There is no problem with researchers. It does everything properly. But when it comes to professors nothing happens. It doesn't even appear at the form.

Thanks and sorry for my bad English big_smile

Re: Editing Multiple Models in One Form

You are missing an opening bracket on the "tr" tag here:

  <% fields_for "professor[]" do |f| %>
  tr><td><br/><br/></td></tr>
  <tr>
  <td><div align="right">?????</div></td>
  <td><div align="left"><%= f.text_field :name %></div></td>
  </tr>
  <% end %>

Does that fix the problem?

<tr><td><br/><br/></td></tr>

Railscasts - Free Ruby on Rails Screencasts

Re: Editing Multiple Models in One Form

I've cut and pasted this entire tutorial, but get this error message:

Showing app/views/projects/edit.rhtml where line #13 raised:

Mysql::Error: Unknown column 'tasks.project_id' in 'where clause': SELECT * FROM tasks WHERE (tasks.project_id = 1)

10: </p>
11: 
12:  <h2>Tasks</h2>
13:   <% for @task in @project.tasks %>
14:      <%= error_messages_for :task %>
15:      <% fields_for "task[]" do |f| %>
16:      <p><%= f.text_field :name %></p>

Re: Editing Multiple Models in One Form

How did you generate the tasks table? Does it have a project_id column?

Railscasts - Free Ruby on Rails Screencasts

Re: Editing Multiple Models in One Form

sorry I didn't reply to you earlier. My problem has been solved.
Thanks

Re: Editing Multiple Models in One Form

thanks for the response, and the great tutorial. I didn't have product_id added. Have you included your migrations somewhere, or explicitly indicated what the database should look like? If not, I think it might be an important addition which would complete your lessons nicely...perhaps I've just missed it...

The first, I assumed, looks like this:
class Projects < ActiveRecord::Migration
  def self.up
    create_table :projects  do  |t|
      t.column  :project_id,  :integer
      t.column  :name,  :string
  end
end

  def self.down
    drop_table  :projects
  end
end


....and the second, like:

class Tasks < ActiveRecord::Migration
  def self.up
    create_table  :tasks  do  |t|
      t.column  :name,  :string
  end
end
  def self.down
    drop_table  :tasks
  end
end



....am I close?

Re: Editing Multiple Models in One Form

Almost, the project_id column needs to go into the tasks table, not the projects table:

  create_table :tasks  do  |t|
    t.column :name, :string
    t.column :project_id, :integer
  end

Railscasts - Free Ruby on Rails Screencasts

Re: Editing Multiple Models in One Form

I don't understand this line:

   @project.tasks.each { |t| t.attributes = params[:task][t.id_to_s] }


The request has "params" hash.  That hash contains an array of tasks.  Hence, params[:task] refers to that array and the [t.id_to_s] indicates which task in the array.  HOWEVER, the array indices should be 0 to # of tasks.  It's not the task ID.  That's why I don't understand why t.id_to_s would work.

For one, in my code, I get an error: undefined method `id_to_s`  Secondly, if this is changed to id.to_s, it doesn't work either.  What is the problem here?

Last edited by robertl (2007-07-07 20:56:43)

Re: Editing Multiple Models in One Form

params[:task] is not an array, it is a hash. so params[:task]["1"] will give the Task with id=1.  That's t.id.to_s not t.id_to_s.

@project.tasks.each checks for all the tasks of @project, so make sure all those tasks are available in the params[:task] hash.

Check your views if you have something like task[1][name].

Re: Editing Multiple Models in One Form

Hi, I'm having a problem with my application when following the steps in the tutorial.

Basically I have this update method in my controller:

  def update
    @recipe = Recipe.find(params[:id])
    @recipe.attributes = params[:recipe]
    @recipe.directions.each { |d| d.attributes = params[:directions][d.id.to_s] }



    if @recipe.valid? && @recipe.directions.all?(&:valid?)
      @recipe.save!
      @recipe.directions.each(&:save!)
      redirect_to :action => 'show', :id => @recipe
    else
      render :action => 'edit'
    end
 
  end


And this is how my view looks:

<p><label for = 'direction'>Directions</label><br/ ></p>
<table>
<% @recipe.directions.each_with_index do |direction, index| %>
<tr>
    <% fields_for "directions[#{index}]", direction do |d| %>
        <td><%= d.text_field :direction, :size => 80 %></td>
    <% end %>
</tr>
<% end %>

I am able to save a list of directions, but when trying to update or add new directions to an existing list, nothing ever gets updated. I checked the SQL statements in the log and it only seems to be updating the list with the old contents. I've spent a while playing around with this, but I don't know what's going on or how to debug it. Any help would be greatly appreciated!

Re: Editing Multiple Models in One Form

I got the forms to work using this tutorial within my controller.  But, I can't get the index action to work.  I can't figure out if my problem is in the controller or the view.  How would one make this @project.tasks display in the index?

Re: Editing Multiple Models in One Form

The author of this series of tutorials is a life saver. I would like to show you all what my update method looks like and its helper methods

Here is the update method.
----------------------------

  def update
    @person = Person.find(params[:id])
    @person.attributes = params[:person]
   
    redefine_save_to_destroy_removed_associations(params, @person, :emails)
    redefine_save_to_destroy_removed_associations(params, @person, :phone_numbers)
    redefine_save_to_destroy_removed_associations(params, @person, :addresses)
   
    update_existing_associations(params, @person, :emails)
    update_existing_associations(params, @person, :phone_numbers)
    update_existing_associations(params, @person, :addresses)
   
    build_populated_associations(params, @person, :emails, [:address])
    build_populated_associations(params, @person, :phone_numbers, [:phone_number])
    build_populated_associations(params, @person, :addresses, [:street1, :street2, :city, :zip])   
       
    if @person.valid?
      Person.transaction do
        @person.save!
        @person.emails.each(&:save!)
        @person.addresses.each(&:save!)
        @person.phone_numbers.each(&:save!)
      end
      redirect_to :action => 'show', :id => @person
    elsif
      render :action => 'edit'
    end
  end

Helper methods in application.rb.
----------------------------
  def build_populated_associations(params, model, association, fields)
    singular = Inflector.singularize(association)
    unless params[singular].nil?
      params[singular].delete_if do |key, value|
        blanks = fields.collect { |field| value[field].blank? } 
        !blanks.include?(false)
      end
     
      params[singular].each_value do |value|
        model.send(association).build(value) if value[:id].blank?
      end
    end
  end
 
  def update_existing_associations(params, model, association)
    singular = Inflector.singularize(association)
    model.send(association).each do |assoc|
      attrs = assoc.attributes
      unless params[singular].nil?
        params[singular].each_value do |value|
          if value[:id] == assoc.id.to_s
            attrs = value
            break
          end
        end
       
        assoc.attributes = attrs
      end
    end 
  end
 
  def redefine_save_to_destroy_removed_associations(params, model, association)
    singular = Inflector.singularize(association)
    param_ids = []
    unless params[singular].nil?
      param_ids = params[singular].values.collect do |value|
        value[:id]
      end
    end
   
    model.send(association).each do |a|
      id = a.id.to_s
      unless param_ids.include?(id)
        def a.save!
          destroy
        end
      end
    end   
  end

Last edited by jwheeler (2007-07-20 23:10:39)

Re: Editing Multiple Models in One Form

I'm having this exact same problem.  Anyone know what is happening here?


miss_michelle wrote:

Hi, I'm having a problem with my application when following the steps in the tutorial.

Basically I have this update method in my controller:

  def update
    @recipe = Recipe.find(params[:id])
    @recipe.attributes = params[:recipe]
    @recipe.directions.each { |d| d.attributes = params[:directions][d.id.to_s] }



    if @recipe.valid? && @recipe.directions.all?(&:valid?)
      @recipe.save!
      @recipe.directions.each(&:save!)
      redirect_to :action => 'show', :id => @recipe
    else
      render :action => 'edit'
    end
 
  end


And this is how my view looks:

<p><label for = 'direction'>Directions</label><br/ ></p>
<table>
<% @recipe.directions.each_with_index do |direction, index| %>
<tr>
    <% fields_for "directions[#{index}]", direction do |d| %>
        <td><%= d.text_field :direction, :size => 80 %></td>
    <% end %>
</tr>
<% end %>

I am able to save a list of directions, but when trying to update or add new directions to an existing list, nothing ever gets updated. I checked the SQL statements in the log and it only seems to be updating the list with the old contents. I've spent a while playing around with this, but I don't know what's going on or how to debug it. Any help would be greatly appreciated!