Rails 4 Nested Resources, Forms, strong params - ruby-on-rails

I am trying to create a workout tracker. I have these nested resources
Routes.rb
resources :days do
resources :workouts
resources :meals
end
My models look fine:
Workout.rb
class Workout < ActiveRecord::Base
has_many :exercises
belongs_to :day
end
Day.rb
class Day < ActiveRecord::Base
has_many :workouts
has_many :meals
accepts_nested_attributes_for :workouts, :allow_destroy => true
accepts_nested_attributes_for :meals, :allow_destroy => true
end
The issue lies when I try to create a new workout..
../views/workouts/_form.html.haml
= form_for([#day, #workout]), html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.hidden_field :day_id, value: params[:day_id], input_html: { class: "form-control"}
= f.submit
I cant seem to save the :day_id the workout is associated with the workout though using strong params to receive them.
WorkoutsController.rb
private
def workout_params
params.require(:workout).permit(:day_id, :name, :mood)
end
def find_day
#day = Day.find(params[:day_id])
end
Instead what gets saved is a nil for :day_id
Rails Console:
Workout id: 11, name: "Hey", mood: "no", day_id: nil, created_at: "2014-12-19 14:53:29", updated_at: "2014-12-19 14:53:29"
HELP?
PS - i tried to do
= form_for([#day, #workout]), as: :foo, html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.hidden_field :day_id, value: params[:day_id], input_html: { class: "form-control"}
= f.submit
and then
def workout_params
params.require(:foo).permit(:day_id, :name, :mood)
end
But it just kept saying the :foo params where empty

The way your routes are set up, I am assuming that the day_id is being passed as a url params:
/days/:day_id/workouts
If that is the case, then you do not need to worry about #day in the form, nor the day_id in the workout_params
Form:
= form_for #workout, html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.submit
Controller:
def create
#workout = #day.workouts.new(workout_params)
....
end
private
def workout_params
params.require(:workout).permit(:name, :mood)
end
def find_day
#day = Day.find(params[:day_id])
end

Related

Ruby on Rail Nested Attributes do not save to database

I am trying to create a form that updates 2 tables - commission_type and commission_tier.
I created the models, controller and form but when I submit it, my commission_tier table does not update. Only my commission_type table updates.
Can someone take a look at my code and tell me what I am doing wrong? I have combed through my code trying to find the mistake, and I cannot find it.
My models
class CommissionType < ApplicationRecord
has_many :commission_tiers
accepts_nested_attributes_for :commission_tiers
end
class CommissionTier < ApplicationRecord
belongs_to :commission_types, optional: true
end
My controller
class Admin::CommissionTypesController < Admin::BaseController
def index
#commission_types = CommissionType.all
end
def new
#commission_type = CommissionType.new
#commission_type.commission_tiers.build
end
def create
#commission_type = CommissionType.new(commission_type_params)
if #commission_type.save
redirect_to admin_commission_types_index_path
else
render "new"
end
private
def commission_type_params
params.require(:commission_type).permit(:name, :active, :allow_mass_update, :plan,
commission_tiers_attributes: [:id, :increment_value, :rate, :commission_type_id])
end
end
My form
<%= simple_form_for #commission_type, url: admin_commission_types_index_path, wrapper: :bootstrap2, :html => { :class => 'form-horizontal' } do |f| %>
<fieldset>
<legend>Properties</legend>
<%= f.input :name, label: 'Commission Name' %>
<%= f.input :active, as: :boolean, label: 'Active?', label_html: { class: 'padding-top' } %>
<%= f.input :allow_mass_update, as: :boolean, label: 'Allow mass update?', label_html: { class: 'padding-top' } %>
<%= f.input :plan, input_html: {id: 'dropdown'},
label: 'Commission Type',
collection: [ ['Select One..', 'select'], ['Flat', 'flat'], ['Flat +', 'flat_plus' ], ['Promotional', 'promotional'], ['Straight', 'straight'], ['Waterfall', 'waterfall'], ['Sliding Scale', 'sliding_scale'] ],
selected: 'select'
%>
</fieldset>
<fieldset id="flat">
<legend>Flat Commission</legend>
<%= f.simple_fields_for :commission_tiers do |builder| %>
<%= builder.input :rate %>
<%= builder.input :increment_value %>
<% end %>
</fieldset>
My form is displaying and working
UPDATE
Some additional details
CommissionType column values = [:name, :active, :allow_mass_update, :plan]
CommissionTier column values = [:id, :increment_value, :rate, :commission_type_id]
Also, when I submit my form, here is an example of what my params are
<ActionController::Parameters {"name"=>"asdf", "active"=>"1", "allow_mass_update"=>"1", "plan"=>"flat", "commission_tiers_attributes"=><ActionController::Parameters {"0"=><ActionController::Parameters {"rate"=>"45"} permitted: true>} permitted: true>} permitted: true>

How to get email member on simple_form from wallet model? [complicated relation]

I have problem. I'm trying to get an email member from wallet_id which that relation not direct to member instead to investor account first like the sample model below.
member.rb
class Member < ActiveRecord::Base
has_one :investor_account, dependent: :destroy
end
investor_account.rb
class InvestorAccount < ActiveRecord::Base
belongs_to :member
has_many :wallets, dependent: :destroy
end
wallet.rb
class Wallet < ActiveRecord::Base
belongs_to :investor_account
end
top_up.rb
belongs_to :wallet
/top_ups/_form.html.slim
= simple_form_for [:transaction, #top_up] do |f|
.form-group
= f.input :wallet_id, collection: #wallet, input_html: { class: 'form-control' }, include_blank: "Select..."
.form-group
= f.input :amount, input_html: { min: 0, value: 0 }
/controllers/top_ups_controller.rb
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'})
end
The data on "f.input :wallet_id ...." was showed up, but it is not as email of the member instead it showing #<Wallet:0x007fd6d795e808> on all wallet dropdown, previously I also write code like below.
= f.input :wallet_id, collection: #wallet, :id, :email, input_html: { class: 'form-control' }, include_blank: "Select..."
But it throwing problem the email not found. My question was how do I pass the member on that #wallet = ... variable to get email member showing on my form? Is there better way to get that?
You can use label_method and value_method params (docs):
= f.input :wallet_id, collection: #wallet, value_method: :id, label_method: :email, input_html: { class: 'form-control' }, include_blank: "Select..."
Also if you need only id and email, there is no need to fetch all the other data from the DB, you can use pluck:
# controller
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'}).pluck(:id, :email)
end
# form
= f.input :wallet_id, collection: #wallet, value_method: :first, label_method: :last, input_html: { class: 'form-control' }, include_blank: "Select..."

Select and save multiple Checkboxes with simple for

I'm trying to implement a multiple select and save with simple form and nested forms.
My view shows all the branches with name and description in a card format (Bootstrap). Next to te name I would like to have a checkbox. I would like to save only the selected ones
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input :branch_id, input_html: { value: branch.id }, as: :radio_buttons
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Here are the associations
class Branch < ApplicationRecord
has_many :my_branch_items
end
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['name'].blank? }
end
class MyBranchItem < ApplicationRecord
belongs_to :my_branch
belongs_to :branch
end
And the Controller
class MyBranchesController < BaseController
before_action :set_my_branch, only: [:show]
def new
#branches = Branch.all
#my_branch = MyBranch.new
end
def create
#my_branch = Quiz.new(my_branch_params)
if #my_branch.save
redirect_to my_branch_path(#my_branch), notice: 'Thanks for taking the Branch Quiz'
else
render :new
end
end
def show
end
private
def set_my_branch
#my_branch = MyBranch.find(params[:id])
end
def my_branch_params
params.require(:my_branch).permit(:name, :note, :user_id, my_branch_items_attributes: MyBranchItem.attribute_names.map(&:to_sym).push(:_destroy))
end
end
Fixed!
Form
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input_field :branch_id, checked_value: branch.id, as: :boolean, boolean_style: :inline, include_hidden: false
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Model was using a validation for blanks field that did not exist in the table.
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items, dependent: :destroy
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['branch_id'].blank? }
end

rails form send many rows from same instance

I have a form to create a teacher, and in this form a have a table where I register the subjects that they can teach and some experience related of it.
But I dont know how to do it, in model, in controller and in view, because a have add nested fields to my teacher form, and use nested_attributes in model, and permit it in controller. but I dont know how to do it..
Model
class Tutor < User
has_many :tutor_subjects
accepts_nested_attributes_for :tutor_subjects
end
In Controller i have the functions...
def create
#tutor = Tutor.new(tutor_params)
#tutor.save
end
def tutor_params
params.require(:tutor).permit(:email,
:first_name,
:last_name,
:gender,
:password,
tutor_subjects_attributes: [])
end
end
And the view
= simple_form_for [:admin, #tutor] do |f|
= f.error_notification
.col-md-8.col-sm-12
.form-inputs
= f.input :email
= f.input :password
= f.input :first_name
= f.input :last_name
= f.input :gender, collection: {Male: 1, Female: 2}, include_blank: "--Select gender--"
%h2.well.well-sm Subjects
%table.table
%thead
%tr
%th Subject
%th Experience
%th Options
%tbody#subjects
= f.simple_fields_for :tutor_subjects_attributes do |s|
%tr.subject0
%td
= s.input :subject_id, collection: Subject.all, label: false, include_blank: "--Select subject--", input_html: { id: "tutor_subjects_attributes_0_subject_id", name: 'tutor[tutor_subjects_attributes][0][subject_id]', class: "subject" }
%td
= s.input :experience, as: :text, label: false, input_html: { placeholder: "Type your experience", name: 'tutor[tutor_subjects_attributes][0][experience]', id: "tutor_subjects_attributes_0_experience" }
%td.options
= link_to :add_subject, "#", id: "add_subject", class: "btn btn-small btn-info"
I am not sure I understand your question right but it seems like you have problems dealing with strong parameters for nested attributes.
Your tutor_params should look like
def tutor_params
params.require(:tutor).permit(
:email,
...
tutor_subjects_attributes: [
:subject_id,
:experience
]
)
end
end

Limit simple_form_for associated records number in Ruby on Rails

I have a large psychological test of 251 question. Each user can complete that test many times. So I created Summary model to represent each completion. Each Summary has many Answers. For each Summary I created a form that represents collection of answers, using Slim templater and simple_form gem:
= simple_form_for(#summary) do |f|
= f.simple_fields_for :answers do |a|
.question
= a.input :question_id, as: :hidden
div= a.object.question.title
- if a.object.question.kind_of? SpiritualityQuestion
ol class="sortable"
- a.object.question.sortable_variants.each do |sortable_variant|
= content_tag_for :li, sortable_variant
= sortable_variant.title
= a.input :text_data, as: :hidden, input_html: { class: 'sortable_data' }
- elsif a.object.question.kind_of? MultilineQuestion
div Time remaining: <span class="time">60</span> s.
= button_tag 'Start', type: 'button', class: 'start_button btn btn-primary'
= a.input :text_data, label: false, input_html: { class: 'span8 timed_text', cols: '60', rows: '20', disabled: true }
- else
= a.association :variant, collection: a.object.question.variants, as: :radio, label: false
br
= f.input :user_id, as: :hidden
= f.input :psy_test_id, as: :hidden
.actions
= f.button :submit, value: 'Save', class: 'btn btn-large btn-success'
And I have related controller action:
#summary = Summary.where(completed: false, user: current_user, psy_test: PsyTest.first)
.includes(:answers => { :question => :variants })
.first_or_initialize
#summary.build_answers if #summary.new_record?
Summary.build_answers:
def build_answers
# Creating answers for questions
psy_test.questions.includes(:variants).each do |q|
answers.new(question: q)
end
end
Now I'm trying to make the test to be paginated, because it's very large and the form generates very slowly. So I want to add limit and offset to answers. Something like that:
= f.simple_fields_for answers.limit(#limit).offset(#offset) do |a|
How it can be made?
I've looked to do field_for source and found simple answer, which I wasn't able find in any guide:
= f.simple_fields_for :answers, #summary.answers.limit(#limit).offset(#offset) do |a|

Resources