Multiple models in the same form in Rails 3.1? - ruby-on-rails

I am using Rails 3.1 and am working on a discussion forum. I have a model called Topic, each of which has many Posts. When the user makes a new topic, they should also make the first Post as well. However, I am not sure how I can do this in the same form. Here is my code:
<%= form_for #topic do |f| %>
<p>
<%= f.label :title, "Title" %><br />
<%= f.text_field :title %>
</p>
<%= f.fields_for :post do |ff| %>
<p>
<%= ff.label :body, "Body" %><br />
<%= ff.text_area :body %>
</p>
<% end %>
<p>
<%= f.submit "Create Topic" %>
</p>
<% end %>
class Topic < ActiveRecord::Base
has_many :posts, :dependent => :destroy
accepts_nested_attributes_for :posts
validates_presence_of :title
end
class Post < ActiveRecord::Base
belongs_to :topic
validates_presence_of :body
end
... but this doesn't seem to be working. Any ideas?
Thanks!

#Pablo's answer seems to have everything you need. But to be more specific...
First change this line in your view from
<%= f.fields_for :post do |ff| %>
to this
<%= f.fields_for :posts do |ff| %> # :posts instead of :post
Then in your Topic controller add this
def new
#topic = Topic.new
#topic.posts.build
end
That should get you going.

A very good explanation from Ryan Bates here and here
For your particular case: you are using a model (:post), instead of an association (:posts) when you call fields_for.
Also check for the proper use of <%= ... %>. In rails 3.x the bahaviour of the construct changed. Block helpers (form_for, fields_for, etc) dont need it, and inline helpers (text_field, text_area, etc) do need it.

Related

multiple nested resources for form_for

I've being following the rails getting started tutorial and have been changing the model to help my understading of rails.
I have an article model which has_many comments:
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5 }
end
class Comment < ActiveRecord::Base
belongs_to :article
end
routes.rb
resources :articles do
resources :comments
end
The view to create the comment is in a partial (as per the rails tutorial)
<%= form_for([#article, #article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
This works fine but I wanted to split this out a bit to make a commenter a separate model (I know its not great OO but I just experimenting here!)
I therefore created a Commenter model:
class Commenter < ActiveRecord::Base
belongs_to :comment
end
changed the Comment to :
class Comment < ActiveRecord::Base
has_one :commenter
belongs_to :article
end
routes.rb
resources :commenters
resources :articles do
resources :comments
end
resources :comments do
resource :commenter
end
I'd like to create the Comment and the commenter at the same time in a single form but I'm stuck on how to change the view to achieve this as the view builds the comments model, do I also need to build the commenter model here? if so how do I achieve this?
<%= form_for([#article, #article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
For what your looking to do, you need to implement Nested Forms.
From the code you have provided, below I have shown how I would change it.
Models
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
accepts_nested_attributes_for :comments # This is required for #article to save the forms nested within it
validates :title, presence: true,
length: { minimum: 5 }
end
class Comment < ActiveRecord::Base
belongs_to :article
accepts_nested_attributes_for :commenter # Required to save nested Commenter form
end
class Commenter < ActiveRecord::Base
belongs_to :comment
end
Article Controller
In the action that will be called when someone decides to comment on an article, you will need to select the #article and create a comment and also a commenter for the comment. These have to be created before the form is rendered, otherwise they won't be displayed.
def create_comment
#article.find(:id_of_article)
#comment = #article.comments.find(:id_of_new_comment)
#comment.create_commenter
end
View
Finally the form
<%= form_for(#article) do |f| %>
<%= f.fields_for(:comments, #comment) do |comment| %> # As article will have many comments, you need to specify the new comment you want to display
<%= comment.label :comment %><br>
<%= comment.text_field :comment %>
<%= comment.fields_for(:commenter) do |commenter| %>
<%= commenter.label :commenter_name %>
<%= commenter.text_field :commenter_name %>
<%end%>
<%end%>
<%= f.submit %>
<% end %>
Anyway, I hope this helps. Although if you're still doing your tutorials, you should learn this fairly soon anyway.

Nested forms and accepts_nested_attributes_for

I am trying to understand how to make a nested form of my models but I am struggeling with understanding how and what I need to do it. I have been reading the Rails documentation and looked at the railscast but they just mention the accepts_nested_attributes_for method etc without explaining. Can someone please help?
Per API of Rails it's said:
Nested attributes allow you to save attributes on associated records through the parent...
Example: it shows how we can manage posts through Member, fields_for is used to manage associated fields in a form, passing it the name of the associated model and then loop through all of the associated post records and create a form builder for each of them.
#controller
def new
#member = Member.new
end
#model
class Post < ActiveRecord::Base
belongs_to :member
end
class Member < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts
end
#form
<%= form_for #member do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :posts do |builder| %>
<p>
<%= builder.label :account %><br />
<%= builder.text_area :account %>
</p>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
Rails API: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

How can I include form elements from other objects?

I'm working a very simple forum software to help get my feet wet with ruby on rails. What I am trying to do is add a text area for the post content when a user creates a new topic, but every time I try to add it in the topic form, I get the following error:
NoMethodError in Topics#new
Showing /Users/Ken/dev/forums/app/views/topics/_form.html.erb where line #11 raised:
undefined method `merge' for :content:Symbol
Here's my new topic form:
<%= form_for #topic do |f| %>
<%= f.error_messages %>
<% if params[:forum] %>
<%= f.hidden_field :forum_id, :value => params[:forum] %>
<% end %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.text_area :post, :content %>
</p>
<p><%= f.submit "Create" %></p>
<% end %>
Here's my Topic model:
class Topic < ActiveRecord::Base
attr_accessible :name, :last_poster_id, :last_post_at
belongs_to :forum
has_many :posts, :dependent => :destroy
end
Here's my Post model:
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :topic
end
How can I get the text area working correctly in the topic form? Do I need to add it to the topic model in order to access it, and if so, how can I do that?
You can use the fields_for helper. See this link, http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for. The first argument could be a Post.new and then you can have the topic text field inside of that block. The end result is a nested form which you can parse in the controller.

Preferred way to create a hierarchy of new ActiveRecord model objects within a single action

Is there a preferred way to create a hierarchy of new ActiveRecord model objects with associations (e.g. creating a model that has_many children) within a single action? Is this just something that should be done in separate bits?
Take the example of a blog post model which has_many comments. I add support for the author of the blog post adding an initial comment within the same form for the blog post. Right now, what I do is have an after_create call in the blog post that checks to see if there is a comment, and the blog post creates a comment if it exists.
I was thinking of just building (.build) the comment with an unsaved blog post, but apparently that does not work since the blog post does not actually have an id yet since it has not yet been saved. I'm interested in finding out what approaches other people have taken.
My preference is for nested model forms.
Models:
class Comment < ActiveRecord::Base
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments
end
Controller:
#post = post.new
#post.comments.build
View:
<% form_for #post do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<% f.fields_for :comments do |builder| %>
<p>
<%= builder.label :content, "Comment" %><br />
<%= builder.text_area :content, :rows => 5 %>
</p>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>

Multiple children in single form in rails

I have a model that has an arbitrary number of children entities. For simplicity lets call the entities Orders and Items. I would like to have a create Orders form where I input the order information, as well as add as many items as I want. If I click the "Add another item" button, a new set of form elements will be added to input the new data, amounts, etc..
I could hack this out in pure javascript, but I'm pretty sure there has to be a more magical, railsish way to do it, maybe with a partial view or something. I'm just a little too new to rails to know what it is.
What is the best way to dynamically add the new form elements, and then to access them in the create controller?
Can't beat this Railscasts.com tutorial provided by Ryan Bates.
Episode 196: Nested Model Form, pt. 1
Here's an example that works with just a single level of nesting
Models
models/company.rb
class Company < ActiveRecord::Base
has_many :people, :dependent => :destroy
accepts_nested_attributes_for :people, :allow_destroy => true
end
models/person.rb
class person < ActiveRecord::Base
belongs_to :company
end
Controllers
companies_controller.rb
def new
#company = Company.new
3.times { person = #company.people.build }
end
Views
views/companies/_form.html.erb
<% form_for #company do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :people do |builder| %>
<%= render "people_fields", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
views/companies/_people_fields.html.erb
<p>
<%= f.label :name, "Person" %>
<%= f.text_field :name %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
</p>

Resources