Re: Creating Two Models in One Form

Thanks.

There's another tutorial which covers this.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

scyrille wrote:

This is definitely a great tutorial !
I've a quick question.
Imagine you want each tasks of your project to be attached to this project. I think you will add
validates_presence_of :project_id within the task model.
However if you do that when there is a validation error you get two nasty messages not very user friendly :
Tasks is invalid
Project can't be blank
On a computer point of view these messages make total sense, however form the user side...
Is there someone knows how to hide validation messages which concerns database relations ?

Thanks
Cyrille

Try

validates_associated :tasks

Re: Creating Two Models in One Form

Does build work with a belongs_to and has_one relationship?  In my model a user has_one :provider and a provider belongs_to :user.

When I try to follow the tutorial I substitute the singular for the plural to reflect the has_one instead of has_many e.g.

@provider = @user.provider.build(params[:provider])

instead of
@provider = @user.providers.build(params[:provider])

but I get an error message noting that "you have a nil object when you didn't expect it" "the error occured while evaluating nil.build"

Any suggestions?

Re: Creating Two Models in One Form

You need to do this:

@provider = @user.build_provider(params[:provider])

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

I am surprised no one has mentioned this. The view can also be coded using fields_for. This has the advantage of being much DRYer/cleaner for longer forms.

# in the projects/new.rhtml
<h1>New Project</h1>

<%= error_messages_for :project %>
<%= error_messages_for :task %>

<%= form_for :project, :url => { :action => 'create'} %>
<p>
Project Name:
<%= f.text_field :name %>
</p>
<%= fields_for :tasks do |detail| do %>
<p>
First Task:
<%= f.text_field :name %>
</p>
<%= end %>
<p>
  <%= submit_tag 'Create' %>
</p>
<%= end %>

Re: Creating Two Models in One Form

Good point. I use fields_for in the second tutorial, but it can be used here as well.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

awesome. but in the controller i just did this.
<code>
def create
@tasks = Task.new(params[:task])
@tasks.project_id = params[:project]
@tasks.save
end
</code>

I think this makes more sense to me, being a noob . remember if you are using drop down menues
<code><%= collection_select("project", "id" , Project.find(:all), "id", "title") %></code>

then in your controller u can just add

<code>
@tasks.project_id = params[:project][:id]
</code>

i donno if its useful or not, just that usually when you are choosing categories, its common to use drop down menus especially if its really long.

Re: Creating Two Models in One Form

Hi, I was trying out your example as given in this post.  After creating couple of new project i found that the task name in the tasks field is set to null.  My code is identical to your code.  Any ideas what can be wrong.

If you want me to copy paste my code here for debuggin purpose let me know.

Cheers
Hari

Re: Creating Two Models in One Form

Thanks to this tutorial, I have conquored a problem I've been having for a week or so. I am faced with another problem. I am working with 3 models and one controller. I have figured out how to display in my list method one of the values associated with my main model. however I can't get the values from the 2nd model displayed. In the list i can get the category displayed but i can't get anything from the menuItemSize table displayed but the name of the model. here is my code in snippets..Thank you..

MODELS
=========================

class MenuItem < ActiveRecord::Base
    belongs_to :category
    has_many :menu_item_sizes

    validates_associated :menu_item_sizes

    validates_presence_of :description, :name
end

class MenuItemSize < ActiveRecord::Base
    belongs_to :menu_item
   
    validates_presence_of :price, :size
    validates_numericality_of :price, :only_integer => true
end

class Category < ActiveRecord::Base
    has_many :menu_items
    validates_presence_of :category

end


CONTROLLER..
==============================
 def create
    @menu_item = MenuItem.new(params[:menu_item])
    @menu_item_size = @menu_item.menu_item_sizes.build(params[:menu_item_size])
   
    if @menu_item.save
     flash[:notice] = 'a new menu item was successfully created.'
      redirect_to :action => 'list'
    else
      render :action => 'new'
    end

  end

def list
    @menu_item_pages, @menu_items = paginate :menu_items, :per_page => 10
   
  end


LIST.rhtml
====================================
<h1>Listing menu_items</h1>

<table border='1' >
  <tr>
    <th>menu Item</th>
    <th>Description</th>
    <th>Category</th>
    <th>size</th>
    <th>price</th>
  </tr>
 
<% for menu_item in @menu_items %>
  <tr>
    <td><%=h(menu_item.name)%></td>
    <td><%=h(menu_item.description)%></td>
    <td><%=h(menu_item.category.category)%></td>
    <td><%=h(menu_item.menu_item_sizes.price)%></td>
   
   

    <td><%= link_to 'Show', :action => 'show', :id => menu_item %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => menu_item %></td>
    <td><%= link_to 'Destroy', { :action => 'destroy', :id => menu_item }, :confirm => 'Are you sure?', :method => :post %></td>
  </tr>
<% end %>
</table>

<%= link_to 'Previous page', { :page => @menu_item_pages.current.previous } if @menu_item_pages.current.previous %>
<%= link_to 'Next page', { :page => @menu_item_pages.current.next } if @menu_item_pages.current.next %>

<br />

<%= link_to 'New menu_item', :action => 'new' %>

Re: Creating Two Models in One Form

any takers....

-B

Re: Creating Two Models in One Form

The problem is you are calling "prices" on an array of menu item sizes. A given menu item can have many sizes, so Rails doesn't know what to do when you call price on many:

<td><%=h(menu_item.menu_item_sizes.price)%></td>

You can either just call it on the first one:

<td><%=h(menu_item.menu_item_sizes.first.price)%></td>

Or loop through them and display multiple prices:

<% for menu_item_size in menu_item.menu_item_sizes %>
  <%= h(menu_item_size.price) %>
<% end %>

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

ryanb wrote:

def create
....
  @task = @project.tasks.build(params[:task])
.....
end

The @project instance variable is set normally, but what's up with how the task is created? We use a fancy method to help us out here. The @project.tasks.build method creates a task (just like Task.new) and adds it to the @project at the same time. Pretty cool huh?

I've tried to modify / adapt this code to the "cookbook" tutorial.

In the category_controller.rb file I added a 'new_mult' (multiple) method :

  def new_mult
    @category = Category.new
    @recipe   = Recipe.new
end

I also added a "create_mult" method :

def create_mult
    @category = Category.new(params[:category])
    @recipe = @category.recipes.build(params[:recipe])
    if @category.save
     redirect_to :action => 'index'
   else
     render :action => 'new_mult'
   end
end

I don't get any errors, but only the category is being added.  (when I call localhost://cook/category/new_mult) from the browser.

I suspect the "@recipe = @category.recipes.build(params[:recipe])" is not
working.

Is the "<variable>.<table_name>.build(....."  method built in? .. or do I need
to define the "...build.." method somewhere?


Mike

Last edited by mstram (2007-05-15 18:28:53)

Re: Creating Two Models in One Form

How is recipe related to category? And are you certain Recipe is passing validation? Lastly, what version of Rails are you using?

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

Found the problem, there was both a 'start_form_tag' and a 'form_tag' both had :action => 'create', I changed one of them to 'create_mult' but not the other ... I don't know why there are two clauses in that file, I removed the second one, and it's working.

Would still like to find out about the 'build' thing .. where is that documented ?

Mike

Re: Creating Two Models in One Form

That's documented in the has_many method.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

Ok, thanks !

Mike

Re: Creating Two Models in One Form

Hi,

I am confused about the lack of transactions here. The example in Pragmatic Rails uses transactions for the save part. Is that redundant or just another way of doing this?

Thanks.

Re: Creating Two Models in One Form

It just so happens Rails handles this properly without a transaction. When the project is saved, it automatically saves the task along with it. If any are invalid it won't save either one.

However, if I redid this tutorial (along with the others on this forum) I would probably use a transaction. First of all because the behavior of saving multiple models in ActiveRecord is not very consistant. For example, while editing the models, saving the parent model (project) will not automatically save the children models. The behavior also varies depending upon the association (has_one vs. has_many). With a transaction it's easy to just save all of the models, and if it comes to a validation error it rolls back the changes.

Railscasts - Free Ruby on Rails Screencasts

Re: Creating Two Models in One Form

Thank-you for this tutorial.

I'm currently editing two models in one form where the models have a has_one/belongs_to relationship (Account has_one AgentDetail. There is validation on both models, however validation errors are not appearing for AgentDetail in the form. Validation is working though, since an account is saved without an associated AgentDetail when AgentDetail fails validation.

My code:


#Accounts Controller
#########
def create
@account = Account.new(params[:account])
@agent_detail = @account.build_agent_detail(params[:agent_detail])     
if @account.save
   redirect_to some_path
else
   render :action => 'new'     
end
end

class AgentDetail < ActiveRecord::Base
  belongs_to :account
  validates_presence_of   :agency_name
end

class Account < ActiveRecord::Base
  has_one  :agent_detail
  validates_presence_of     :login, :email
end


<%= error_messages_for :account, :agent_detail %>
<% form_for :account, :url => accounts_path do |f| -%>
  <legend>Account Details</legend>
  <p><label for="login">Login</label><br/>
  <%= f.text_field :login %></p>

  ....
 
  <% fields_for :agent_detail do |d|  %>   

  ....

  <legend>Agent Details</legend>
  <p><label for="agency_name">Agency name</label><br/>
  <%= d.text_field :agency_name %></p>

  ....


Can somebody suggest why validation errors for the agency detail model are not showing up?

Many thanks

Re: Creating Two Models in One Form

when editing a model it works a little differently. The child (agency detail) doesn't automatically get saved/validated so you need to do that manually. You can do this with a transaction.

def create
  @account = Account.new(params[:account])
  @agent_detail = @account.build_agent_detail(params[:agent_detail])   
  do Account.transaction
    @account.save!
    @agent_detail.save!
  end
  redirect_to some_path
rescue ActiveRecord::RecordInvalid
  render :action => 'new'
end

Railscasts - Free Ruby on Rails Screencasts