I have a form I would like to create that populates the corresponding model form for, and a fields_for that populates a has many through table.
The plan.rb model:
class Plan < ApplicationRecord
has_many :plan_materials
has_many :materials, through: :plan_materials
accepts_nested_attributes_for :materials
end
The materials.rb model:
class Material < ApplicationRecord
has_many :plan_materials
has_many :plans, through: :plan_materials
end
And the PlanMaterial model:
class PlanMaterial < ApplicationRecord
belongs_to :plan
belongs_to :material
end
This is what I have in the plan form:
<%= form_for #plan do |form| %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: 'form-control' %>
</div>
<%= form.fields_for :materials, plan.materials.build do |material_fields| %>
<%= material_fields.text_field :title %>
<% end %>
<%= form.submit %>
<% end %>
I have created fields_for forms before but never trying to input the information for the form being created, and the id of the material they are selecting, in a new table from that same form.
I am creating this through a plan.materials.build which I realize could possibly be the wrong way to go about doing this because I don't think it would be building that in the plan_materials table.
class PlansController < ApplicationController
def index
#plans = Plan.all
end
def new
#plan = Plan.new
#plan.materials.build
end
def create
#plan = Plan.new(plan_params)
respond_to do |format|
if #plan.save
format.html { redirect_to plan_path, notice: 'Plan has been created' }
else
format.html { render :new, notice: 'There was an error saving your plan' }
end
end
end
private
def plan_params
params.require(:plan).permit(:name, :description, :grade_id, :subject_id, :unit_id, materials_attributes: [:title])
end
end
So, just to recap, I would like to create a plan, and be able to add materials to that plan. I need to have that plan created in the plans table, along with the id of the plan that was created and the id of the material that was added in that form. How do I go about doing this?
Related
I have two models, Hotel and Room. I want to create a form that will allow me to add a new hotel and rooms, which indicates the name of the hotel and the quantity of rooms.
I know I should use "nested form", but it is hard for me to implement it properly.
Here is my code:
HotelsController
class HotelsController < ApplicationController
def index
#hotels = Hotel.all
end
def new
#hotel = Hotel.new
end
def create
#hotel = Hotel.new(hotel_params)
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
def show
#hotel = Hotel.find(params[:id])
end
def destroy
#hotel = Hotel.find(params[:id])
#hotel.destroy
redirect_to hotels_url, notice: 'Hotel was successfully destroyed.'
end
private
def hotel_params
params.require(:hotel).permit(:name, :rooms_count)
end
end
Hotel model
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
end
Room model
class Room < ApplicationRecord
belongs_to :hotel, optional: true # avoiding rails 5.2 belongs_to error
end
form
<%= form_with scope: :hotel, url: hotels_path, local: true do |form| %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.label :rooms_count %><br>
<%= form.number_field :rooms_count %>
</p>
<p>
<% form.fields_for :rooms do |f|%>
<p>
**THE CODE**
</p>
<% end %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
You forgot the "ERB echo sign" (=) on the fields_for helper.
Correct form:
<%= form.fields_for :rooms do |f|%>
I have found a solution to my question.
def create
#hotel = Hotel.new(hotel_params)
#hotel.rooms_count.times do
#hotel.rooms.build
end
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
It implement a #hotel.rooms.build as many times as #hotel.rooms_count number entered in Rooms Count field in form.
I'm trying to create a form where users can see a list of their friends and add or remove friends from lists (sort of like facebook's groups). For the create list view, I can get the form working properly, but for the edit view I'm having trouble with check_box syntax and fields_for.
routes.rb
resources :friendships
resources :friend_lists
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
has_many :friend_lists
has_many :flist_memberships, class_name: 'FlistMembership', foreign_key: 'member_id', dependent: :destroy
...
end
class FriendList < ActiveRecord::Base
belongs_to :user
has_many :flist_memberships
has_many :members, through: :flist_memberships
accepts_nested_attributes_for :members, allow_destroy: true
accepts_nested_attributes_for :flist_memberships, allow_destroy: true
end
class FlistMembership < ActiveRecord::Base
belongs_to :friend_list
belongs_to :member, class_name: 'User'
end
class FriendListsController < ApplicationController
def new
#friend_list = current_user.friend_lists.build
#friends = current_user.friends.paginate(page: params[:page])
#friend_list.flist_memberships.build
end
def create
#friend_list = current_user.friend_lists.build(friend_list_params)
respond_to do |format|
format.html {
if #friend_list.save
flash[:success] = "Friends list created!"
redirect_to friendships_path
else
render 'friend_lists/new'
end
}
end
end
def edit
#friend_list = FriendList.find(params[:id])
#friends = current_user.friends
end
def update
#friend_list = FriendList.find(params[:id])
if #friend_list.update_attributes(friend_list_params)
flash[:success] = "Friend list updated!"
redirect_to friend_list_path(#friend_list)
else
render 'edit'
end
end
private
def friend_list_params
params.require(:friend_list).permit(:name, flist_memberships_attributes: [:id, :member_id, :friend_list_id])
end
end
new.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<% #friends.each do |friend| %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :member_id, {}, friend.id %>
<%= m.label :member_id, friend.name %>
<% end %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
edit.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.collection_check_boxes(:member_id, #friends.all, :id, :name) %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
new.html.erb renders and saves the selected items correctly, but I can't figure out how to write the edit form so that it doesn't iterate through the collection once for each item in the collection (both with collection_check_boxes and the form as written in new.html.erb). Trying
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :id %>
<%= m.label :id, :name %>
<% end %>
in the edit form just passes the string "id" as params rather than pulling the id of each member in the collection, and I'm not sure how to pull the individual item's name/id without iterating over the collection, which goes back to the multiple renders problem. I'm using cocoon elsewhere in the project, but for this I'd rather present the user a list of all options and allow them to check a box for each one, rather than having to manually add each item on the list. If there is a way to do it with cocoon, I'd be happy to hear it.
I want to build a many-to-many association through tagging between posts and tags. Users can create posts with tags by checking the existed tags. But I don't know how to create a nested form and save the association.
My form
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.label :text %><br />
<%= f.text_field :text %>
</div>
<div>
<%= hidden_field_tag "post[tag_ids][]", nil %>
<% Tag.all.each do |tag| %>
<%= check_box_tag "post[tag_ids][]", tag.id, #post.tag_ids.include?(tag.id) %>
<%= tag.name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
posts controller here:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:text)
end
end
Model
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :post
end
I don't think you actually need to have a nested form in this case. Rails automagically figures out the association, when given the attribute :tag_ids
This is what I did for a multiselect form_helper, which would need minor changes for a check_box helper
f.select :tag_ids, Tag.all.collect {|tag| [tag.name, tag.id]}, {}, :multiple => true
I have a Cart model which has_many line_items. I am trying to update the quantity attribute for a line_item object in a cart but it doesn't seem to be updating. I want the quantity to update and redirect right back to the same page. Below is my code, when I submit the form it just redirects to the same page with the quantity value unchanged
models
class LineItem < ActiveRecord::Base
attr_accessible :cart_id, :product_id, :quantity, :unit_price, :product, :cart,
:color_id, :size_id, :extra_id
belongs_to :cart
belongs_to :product
belongs_to :color
belongs_to :size
belongs_to :extra
validates :quantity, :presence => true
class Cart < ActiveRecord::Base
attr_accessible :purchased_at
has_many :line_items
has_one :order
controllers
class LineItemsController < ApplicationController
def new
#line_item = LineItem.new
end
def create
#line_item = LineItem.create!(params[:line_item].merge(:cart => current_cart))
#line_item.update_attributes!(:unit_price => #line_item.item_price)
redirect_to current_cart_url
end
def update
#line_item = LineItem.find(params[:id])
redirect_to current_cart_url
end
end
class CartsController < ApplicationController
def show
#cart = current_cart
end
def update
#cart = current_cart
#line_item = #cart.line_items.find(params[:id])
#line_item.update_attributes(:quantity => params[:quantity])
redirect_to current_cart_url
end
end
routes
get 'cart' => 'carts#show', :as => 'current_cart'
carts/show
<% for line_item in #cart.line_items %>
<div class="row">
<div class="span6">
<%=h line_item.product.name %>
</div>
<div class="span2">
<%= form_for #cart do |f| %>
<%= f.number_field :quantity, :value => line_item.quantity, class: "qty" %>
<%= f.hidden_field :line_item_id, :value => line_item.id %>
<%= f.submit "update" %>
<% end %>
</div>
<div class="span2"><%= number_to_currency(line_item.unit_price) %></div>
<div class="span2"><%= number_to_currency(line_item.full_price) %></div>
</div>
<% end %>
Any insight well appreciated
You've got a form_for #cart and you're trying to update an attribute on a line_item.
Change it to form_for line_item, get rid of your hidden field, and add #line_item.update_attributes(params[:line_item]) to LineItemController#update and it will work so long as line_items are defined as a resource.
That's the easiest way. The best way may be to use accepts_nested_attributes_for :line_items in your Cart model and use fields_for in your form.
I'm working on my first Rails project, and I have the following model relationship:
class Profile < ActiveRecord::Base
belongs_to :identifiable, polymorphic: true
accepts_nested_attributes_for :students
class Student < ActiveRecord::Base
has_one :profile, as: :identifiable
attr_accessible :profile
The associated controllers are:
class StudentsController < ApplicationController
def new
#student = Student.new
end
def create
#student = Student.new(params[:student])
if #student.save
redirect_to root_path
else
render 'new'
end
end
end
And
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
#profile.save
end
end
What I'm trying to do is create a new Student with the following form, which is in students\new.html.erb:
<h1>Create a new Student Account</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#student) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.fields_for :profile, #profile do |builder| %>
<%= builder.label :name %>
<%= builder.text_field :name %>
<%= builder.label :email %>
<%= builder.text_field :email %>
<%= builder.label :password %>
<%= builder.password_field :password %>
<%= builder.label :password_confirmation, "Confirmation" %>
<%= builder.password_field :password_confirmation %>
<% end %>
</div>
</div>
<p><%= f.submit "Submit", class: "btn btn-large btn-primary" %></p>
<% end %>
I'm getting the following error message when I try to submit the form: No association found for name 'students'. Has it been defined yet? What am I doing wrong? Thanks in advance.
In order for a model to accept nested attributes for another model, the association to the other model needs to be declared. In Profile you have accepts_nested_attributes_for :students, but there is no corresponding association defined (e.g. has_many :students), which is why you're getting that particular error. In your case, this association wouldn't be correct, however.
Usually, if model A accepts nested attributes for model B, either A has_many B or A has_one B. In your case, you have A belongs_to B. A better design would be
class Profile < ActiveRecord::Base
belongs_to :identifiable, polymorphic: true
class Student < ActiveRecord::Base
attr_accessible :profile_attributes
has_one :profile, as: :identifiable
accepts_nested_attributes_for :profile
Should your student be singular? ie: accepts_nested_attributes_for :student
Edit: Also, your Student should accept nested attributes for a Profile, if the Student has_one profile, and the Student form contains the fields_for call (I think...)