How do I save a form in double nested form? - ruby-on-rails

I have three models: Event, Workout, and Round.
A person can create a event, that includes a workout, and configure number of sets and weights through round.
I am currently using cocoon gem to create a nested form. I am able to use the form and save Event and Workout, however, Round is not being saved.
class Event < ActiveRecord::Base
has_many :workouts, dependent: :destroy
has_many :rounds, :through => :workouts
belongs_to :user
accepts_nested_attributes_for :workouts, :allow_destroy => true
end
class Workout < ActiveRecord::Base
belongs_to :event
has_many :rounds, dependent: :destroy
accepts_nested_attributes_for :rounds, :allow_destroy => true
end
class Round < ActiveRecord::Base
belongs_to :workout
belongs_to :event
end
I currently have my routes set like this.
Rails.application.routes.draw do
devise_for :users
root 'static_pages#index'
resources :events do
resources :workouts do
resources :rounds
end
end
In my controller, this is how I have my new methods.
New Method for Event
def new
#event = current_user.events.new
end
New Method for Workout
def new
#workout = Workout.new
end
New Method for Round
def new
#round = Round.new
end
I currently have the form under Events' view folder. Under show.html.erb of Events view file, I am trying to display Rounds as well by
<% #workout.rounds.each do |round| %>
<%= round.weight %>
<% end %>
But I am getting undefined method for rounds. Is it not possible to display round in Event view?
Thanks for the help!
Edit 1:
Here is my nested forms.
At the top, I have form for Event.
<%= simple_form_for #event do |f| %>
<%= f.input :title %>
<h3>Work Outs</h3>
<div id="workouts">
<%= f.simple_fields_for :workouts do |workout| %>
<%= render 'workout_fields', f: workout %>
<% end %>
<div class="links">
<%= link_to_add_association 'add workout', f, :workouts %>
</div>
</div>
<div class="field">
<%= f.label :start_time %><br>
<%= f.datetime_select :start_time %>
</div>
<div class="field">
<%= f.label :end_time %><br>
<%= f.datetime_select :end_time %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit "Create new Workout" %>
</div>
<% end %>
<% end %>
</form>
Then there is a form for Workout
<div class="nested-fields">
<%= f.input :name %>
<div class="rounds">
<%= f.simple_fields_for :rounds, :wrapper => 'inline' do |round| %>
<%= render 'round_fields', f: round %>
<% end %>
<div class="links">
<%= link_to_add_association 'add round', f, :rounds, :render_option => { :wrapper => 'inline' } %>
</div>
</div>
<%= link_to_remove_association "remove workout", f %>
</div>
And finally, I have the form for Round
<div class="nested-fields">
<table class="table round-table">
<tr>
<td>
<% index = 0 %>
<%= f.simple_fields_for :rounds do |round| %>
<%= render 'set_fields', {f: round, index: index} %>
<% index = index + 1 %>
<% end %>
</td>
<td>Previous Weight</td>
<td><%= f.input_field :weight %></td>
<td><%= f.input_field :repetition %></td>
<td><%= link_to_remove_association "remove rounds", f %></td>
</tr>
</table>
</div>
I am able to create round on rails console and save them. But when I use the form on the web, I cannot save them.
EDIT 2
This is currently how I have the event_params and workout_params set-up.
def event_params
params.fetch(:event, {}).permit(:title, :description, :start_time, :end_time, :workouts_attributes => [:id, :name, :category, :_destroy])
end
Then the workout_params:
def workout_params
params.require(:workout).permit(:name, :category, :rounds_attributes => [:id, :weight, :set, :repetition, :_destroy])
end
I am confused why the form would save Event and Workout. But Round always returns an empty array.

Finally solved it!
I had to have an association in the params as well. Double nested association.
def event_params
params.fetch(:event, {}).permit(:title, :description, :start_time, :end_time, :workouts_attributes => [:id, :name, :category, :_destroy])
end
For my event_params, I only had :workouts_attributes. I thought having :rounds_attributes in workout_params would be okay. But I needed to have rounds_attributes in event_params as well.
Fixing it like below fixed the issue.
def event_params
params.fetch(:event, {}).permit(:title, :description, :start_time, :end_time, :workouts_attributes => [:id, :name, :category, :_destroy, :rounds_attributes => [:id, :weight, :set, :repetition, :_destroy]])
end

you already have rounds attribute in Events model (has_many :rounds, :through => :workouts), why not use it?
<% #event.rounds.each do |round|
<%= round.weight %>
<% end %>

Related

Validation failed: Stocks product must exist, Stocks location must exist

I have three models -> locations, products and stocks.
Stocks is a join table of locations and products.
When creating a new product i used fields_for to show locations and even though i got it to work, for some reason now it does not seem to work anymore and it gives me the above error.
<div class="input-field">
<%= f.label :product_name %>
<%= f.text_field :name, autofocus: true %>
</div>
<div class="input-field">
<%= f.label :price %>
<%= f.text_field :price, autofocus: true %>
</div>
<% if !#edit %>
<%= f.fields_for :stocks do |ff| %>
<div class="input-field margin-top x-4">
<%= ff.collection_select :location_id, Location.all, :id, :structured_location , {:prompt => "Please Select Locations for Product"}, {multiple: true} %>
<%= ff.label :locations %>
</div>
<div class="input-field">
<%= ff.label :quantity %>
<%= ff.text_field :quantity %>
</div>
<div class="input-field">
<%= ff.label :threshold_quantity %>
<%= ff.text_field :threshold_quantity %>
</div>
<% end %>
<% else %>
<%= collection_select :product, :location_ids, Location.all, :id, :structured_location , {:prompt => "Please Select Locations for Product"}, {multiple: true} %>
<% end %>
<div class="row margin-top x-4">
<div class="col s12 center-align">
<%= f.submit "#{current_page?(new_product_path) ? "Create Product" : "Update Product"}", class: "btn wave-effect pink darken-1 btn-large" %>
</div>
</div>
controller
class ProductsController < ApplicationController
helper_method :sort_column, :sort_direction
def index
#products = Product.order(sort_column + " " + sort_direction)
end
def new
#product = Product.new
#product.stocks.build
end
def create
#product = Product.new(product_params)
if #product.save!
flash[:notice] = "Successfully saved..."
redirect_to products_path
else
flash[:alert] = "Something went wrong, please check the values you entered"
redirect_to :back
end
end
private
def product_params
params.require(:product).permit(:name,:price, location_ids: [], stocks_attributes: [:id, :quantity, :threshold_quantity, location_id: []])
end
end
product model
class Product < ApplicationRecord
has_many :stocks, dependent: :destroy
has_many :locations, :through => :stocks
accepts_nested_attributes_for :stocks
end
parameters in rails console
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"l1BFhrdyB2QMO5k3+60GNiPphFfF+DXDGPbUU3V2Op2aekObjgIe13k8uoedmDIEZgIeXPZUeS/0VxQXkKa1Uw==",
"product"=>{"name"=>"Soap", "price"=>"10", "location_ids"=>["", "1",
"2"], "stocks_attributes"=>{"0"=>{"quantity"=>"100",
"threshold_quantity"=>"100"}}}, "commit"=>"Create Product"}
After hours of searching i stumbled upon this post
BigBinary
it seems that Rails 5 made belongs_to relationship IDs required by default thats why i had validations failed and i couldn't find anything about it.
simply adding optional: true in my stock model worked!
class Stock < ApplicationRecord
belongs_to :location, optional: true
belongs_to :product, optional: true
accepts_nested_attributes_for :product, allow_destroy: true
accepts_nested_attributes_for :location, allow_destroy: true
end

rails nested attributes not save to images table

i'm trying to save two object (image and event) using one form (event form),
but the when i submit the button there is only event object that created, the images object not save to images table,
models/event.rb
has_one :images, :class_name => '::Refinery::Image'
accepts_nested_attributes_for :images, :allow_destroy => true
models/image.rb
belongs_to :events, class_name: 'Refinery::Events::Event'
events_controller.rb
def new
#event = Event.new
#event.build_images
end
def create
#event = Event.new(event_params)
end
def event_params
params.require(:event).permit(:nama, :deskripsi, images_attributes:[:id, :image_name, :image_size, :image_width, :image_height, :created_at, :updated_at, :image_mime_type, :image_uid, :image_title, :image_alt, :event_id] )
end
events/_form.html.erb
<%= form_for [refinery, :events, #event], :html => { :multipart => true } do |f| %>
<%= render '/refinery/admin/error_messages',
:object => #event,
:include_object_name => true %>
<div class='field nama_field string_field'>
<%= f.label :nama %>
<%= f.text_field :nama %>
</div>
<div class='field deskripsi_field text_field'>
<%= f.label :deskripsi %>
<%= f.text_area :deskripsi, :rows => 8 %>
</div>
<%= f.fields_for :images do |ff| %>
<div>
<%= ff.label :images %>
<%= ff.file_field :images %>
</div>
<% end %>
<div class='actions'>
<%= f.submit t('.send') %>
</div>
<% end %>
anyone can help me why the images not saved into images table?

Cocoon - Wrong number of arguments (1 for 0) for look-up or create :belongs_to

Following the Cocoon wiki for implementing The look-up or create :belongs_to I'm receiving the error: wrong number of arguments (1 for 0). I'm not exactly sure what its referring to being that I'm following the tutorial verbatim aside from using slim as my precompiler. Here's what my code looks like:
Models
class Project < ActiveRecord::Base
belongs_to :user
has_many :tasks
accepts_nested_attributes_for :tasks, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :user, :reject_if => :all_blank
end
class User < ActiveRecord::Base
has_many :projects
end
Projects Form
<%= simple_form_for #project do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<h3>Tasks</h3>
<div id="tasks">
<%= f.simple_fields_for :tasks do |task| %>
<%= render 'task_fields', :f => task %>
<% end %>
<div class="links">
<%= link_to_add_association 'add task', f, :tasks %>
</div>
</div>
<div id="user">
<div id="user_from_list">
<%= f.association :user, collection: User.all(:order => 'name'), :prompt => 'Choose an existing user' %>
</div>
<%= link_to_add_association 'add a new person as owner', f, :user %>
</div>
<%= f.submit %>
<% end %>
Projects Controller
...
def project_params
params.require(:project).permit(:name, :description, tasks_attributes: [:id, :description, :done, :_destroy], user_attributes: [:id, :name])
end
Backtrace
app/views/projects/_form.html.erb:16:in `block in _app_views_projects__form_html_erb___3132123068035883478_70337216288160'
app/views/projects/_form.html.erb:1:in `_app_views_projects__form_html_erb___3132123068035883478_70337216288160'
app/views/projects/new.html.erb:3:in `_app_views_projects_new_html_erb__2418839848133678570_70337176808940'
ActiveRecord#all has been changed in rails 4 - this is now doing what scoped used to do. It does not expect any extra params. Instead of User.all(order: 'name') do:
User.order(:name)

Rails - :_destroy method not working

I am trying to use :_destroy method in a nested form, but it just does not work
There are two models:
class Setting < ActiveRecord::Base
attr_accessible :category, :name, :setting_items_attributes, :_destroy
attr_accessor :_destroy
has_many :setting_items, :dependent => :destroy
accepts_nested_attributes_for :setting_items, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class SettingItem < ActiveRecord::Base
attr_accessible :setting_id, :value
belongs_to :setting
end
In the controller I create a instance:
def edit
#setting = Setting.find(params[:id])
#setting.setting_items.build
end
And the form looks like this:
<%= form_for(#setting) do |f| %>
<div class="field">
<%= f.label :category %>
<%= f.text_field :category %>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<hr>
<h3>Params:</h3>
<%= f.fields_for :setting_items do |s| %>
<span>
<div class="fields">
<%= s.text_field :value %>
<%= s.hidden_field :_destroy %>
<%= link_to_function "delete", "remove_fields(this)"%>
</div>
<% end %>
<div class="actions">
<%= f.submit "Update", :class => "btn btn-primary"%>
</div>
<% end %>
Also the function I use is here:
function remove_fields(link){
$(link).prev("input[type=hidden]").val("1");
$(link).parent().fadeOut("slow");
}
So the setting_items form is simply not working at all, It shows the update is successful, but nothing is actually added or deleted.
for example->your model associations are as follows: just follow the below steps to make use of magical _destroy attribute
####parent model
plan.rb
has_many :members,:dependent => :destroy
#this is important you want to destroy nested records
accepts_nested_attributes_for:members,:allow_destroy => true
attr_accessible :members_attributes
##child model
member.rb
belongs_to :plan
######controller
def edit
#plan.Plan.find(params[:id])
end
#####edit.html.erb
<%= form_for #plan do |f| %>
<%= f.fields_for :members do |member| %>
<div class="member">
<%= member.text_field :title%>
<%= image_tag 'delete.png',:class =>'remove_member',:id=>member.id %>
<!-- we need to set this hidden field value as 1 to mark it to be deleted during save/update of parent-->
<%= member.hidden_field :_destroy, :class => 'delete_member', :member_id => member.id %>
<!--similar to
<input id="plan_members_attributes_0__destroy" class="delete_member" type="hidden" value="false" name="plan[members_attributes][0][_destroy]" member_id="#{id of member}">
-->
</div>
<%end%>
<%end%>
##add js onclick of remove button/image
$('.delete_member').click(function(){
//remove div from screen
$(this).closest('.member').remove();
//get relevant id to remove/mark as delete
id =jQuery(this).attr('id');
//remove/mark the nested model/record as ready for deletion for rails by adding true/1 value
$("input[member_id="+id+"]").attr('value',1);
})

Can't mass-assign protected attributes: _destroy - Ruby on Rails

I'm getting this error:
"ActiveModel::MassAssignmentSecurity::Error in DaysController#create
Can't mass-assign protected attributes: _destroy"
I didn't even know that _destroy is an attribute!
What I have going on:
My model is that I have "Trips" which has many "Days"
In the "Show" view of my Trips model, I'm rendering a partial for a Form to add a new "Day":
<div id="day_form">
<%= render :partial => "day_form", :day => #day %>
</div>
My model:
class Trip < ActiveRecord::Base
attr_accessible :title, :days_attributes
has_many :days
accepts_nested_attributes_for :days, allow_destroy: true
end
class Day < ActiveRecord::Base
attr_accessible :activity_id, :order, :summary, :trip_id, :activities_attributes
belongs_to :trip
has_many :activities, :order => 'position'
accepts_nested_attributes_for :activities, allow_destroy: true
end
When I submit the form, I am getting this Mass Assignment error. Why?
EDIT
The 'Day' Form looks like this:
<%= form_for(#day) do |f| %>
<ul>
<% #day.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<fieldset>
<%= f.label :summary, "Day Summary" %><br />
<%= f.text_area :summary, :rows => 1 %><br />
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
module ApplicationHelper
def link_to_remove_fields(name, f)
text_field_tag(:_destroy) + link_to_function(name, "remove_fields(this)")
end
replace f.hidden_field

Resources