Rails has_one relationship and simple_form - ruby-on-rails

Using simple_form to build a nested form for a WorkoutSet model with the following fields:
intro_video
get_ready
outro_video
WorkoutStep (another nested model)
I want to be able to select an intro_video out of a select
As WorkoutSet is inside Workout, the forms look like this:
WorkoutSet.rb
class WorkoutSet < ActiveRecord::Base
has_and_belongs_to_many :workout_steps, :join_table => :sets_steps, dependent: :destroy
has_and_belongs_to_many :workouts, :join_table => :workout_sessions
accepts_nested_attributes_for :workout_steps, allow_destroy: true
has_one :intro_video_usage, class_name: 'VideoUsage::Intro', as: :parent, dependent: :destroy
has_one :intro_video, through: :intro_video_usage, source: :video
accepts_nested_attributes_for :intro_video
has_one :get_ready_video_usage, class_name: 'VideoUsage::GetReady', as: :parent, dependent: :destroy
has_one :get_ready_video, through: :get_ready_video_usage, source: :video
has_one :congrats_video_usage, class_name: 'VideoUsage::Congratulations', as: :parent, dependent: :destroy
has_one :congrats_video, through: :congrats_video_usage, source: :video
end
_form.html.haml
%h2 Sets
.sets.some{ :style => "margin-left: 25px" }
= f.simple_fields_for :workout_sets do |set|
= render 'workout_set_fields', f: set
.links
.button-row
= link_to_add_association 'add set', f, :workout_sets
_workout_set_fields.html.haml
.nested-fields
%h2 New set - #{link_to_remove_association 'Delete', f}
= f.label :title
= f.text_field :title
= f.input :intro_video, :collection => Video.all, :label_method => :title, :value_method => :id, :include_blank => false
%br
%br
#sets.some{ :style => "margin-left: 25px" }
= f.simple_fields_for :workout_steps do |step|
= render 'workout_step_fields', f: step
.links
.button-row
= link_to_add_association 'add step', f, :workout_steps
The select is rendered and the values looks fine until I save the model whitelisting the following parameters:
params.require(:workout).permit(:title, :pro, :image, warmup_attributes: [:id, :_destroy, {main_video_ids: []}], workout_sets_attributes: [:id, :_destroy, :title, :intro_video, workout_steps_attributes: [:id, :_destroy, {main_video_ids: []}] ])
Also, I notice that in the line f.input :intro_video the parameter being sent is intro_video, instead of intro_video_attributes
Thanks in advance.

Related

nested_attributes with simple_field_for getting unpermitted parameters error rails

I have models
class Profile < ActiveRecord::Base
belongs_to :user
has_many :user_interests
has_many :interests, :through => :user_interests
accepts_nested_attributes_for :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :profile
belongs_to :interest
end
class Interest < ActiveRecord::Base
has_many :user_interests
has_many :profiles, :through => :user_interests
end
controller
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :date_of_birth, user_interests_attributes: {:interest => []})
end
view
=#"http://stackoverflow.com/questions/18428871/multi-column-forms-with-fieldsets"
= simple_form_for(#profile, html: {class: 'form-horizontal'}) do |f|
.well.well-lg
%fieldset
%legend Personal Information
.row
.col-sm-4
.form-group
= f.input :first_name, label: 'First Name*'
= f.hint 'No special characters, please!'
.col-sm-4
.form-group
= f.input :last_name, label: 'First Name*'
= f.hint 'No special characters, please!'
.row
.col-sm-4
.form-group
= f.input :date_of_birth, as: :string, 'data-behaviour'=>'datepicker'
%legend Other Information
.row
.col-sm-4
.form-group
= f.simple_fields_for :user_interests, UserInterest.new do |s|
= s.collection_select(:interest, Interest.all, :id, :name,{},{:multiple=>true})
= f.hint 'Please use Ctrl key on your keyboard to select multiple items'
.row
= f.submit
But getting error unpermitted parameters
profile_params
Unpermitted parameters: interest
=> {"first_name"=>"", "last_name"=>"", "date_of_birth"=>"", "user_interests_attributes"=>{"0"=>{}}}
where my params are:
params
=> {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"7/7sKljbi88cmUOen/WFWzhzV6exE8I8fBnNMA5EELw=",
"profile"=> {"first_name"=>"", "last_name"=>"",
"date_of_birth"=>"",
"user_interests_attributes"=>{"0"=>{"interest"=>"test"}}},
"commit"=>"Update Profile", "action"=>"update",
"controller"=>"profiles", "id"=>"1"}
Please correct me where I am wrong
You forgot to add accepts_nested_attributes_for :interest on UserInterest.
class Profile < ActiveRecord::Base
belongs_to :user
has_many :user_interests
has_many :interests, :through => :user_interests
accepts_nested_attributes_for :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :profile
belongs_to :interest
accepts_nested_attributes_for :interest
end
class Interest < ActiveRecord::Base
has_many :user_interests
has_many :profiles, :through => :user_interests
end
You might have to define interest as a valid parameter on strong_parameters definition in your controller.

Has_one Association breaking in Edit

I have a nested form using the cocoon gem that gests 5 classes deep. They are all belongs_to and has_many except one that is the 2nd to most nested class with a has_one and belongs_to association. Whenever I edit it, it gets deleted and recreates an instance (not what I want) any ideas?
class FirmwaresController < ApplicationController
def index
#firmwares = Firmware.all
end
def new
#firmware = Firmware.new
end
def edit
#firmware = Firmware.find(params[:id])
end
end
class Setting < ActiveRecord::Base
belongs_to :menu_item
has_many :selections, dependent: :destroy
has_many :dependencies
attr_accessible :kind, :name, :placement, :selections_attributes
accepts_nested_attributes_for :selections, reject_if: :all_blank, allow_destroy: true
validates_presence_of :kind
end
class MenuItem < ActiveRecord::Base
belongs_to :menu
belongs_to :dependency
has_one :setting, dependent: :destroy
attr_accessible :dependency_id, :menu_for, :name, :placement, :setting_attributes
accepts_nested_attributes_for :setting, reject_if: :all_blank, allow_destroy: true
validates_presence_of :name
end
_menu_items.html.haml
.row-fluid
.input-prepend
= f.input :name, label: "Menu Item Name"
.input-append
= f.input :placement, label: "Order in Menu (X.Y)", as: :string
= f.simple_fields_for :setting do |settings_form|
= render 'setting_fields', f: settings_form
%p
= link_to_add_association "Has Setting?", f, :setting, class: "btn btn-primary btn-small add_setting_link"
= link_to_remove_association 'Remove Menu Item', f, class: "btn btn-danger btn-small remove_menu_item"
_setting_fields.html.haml
.row-fluid
.input-prepend
= f.input :kind, label: "Type", collection: setting_kinds, input_html: {class: "setting_type"}
.input-append
= link_to_remove_association 'Remove Setting', f, class: 'btn btn-danger btn-small remove_setting_link'
.setting_selection{style: "display: none;"}
= f.simple_fields_for :selections do |selections_form|
= render 'selection_fields', f: selections_form
%p= link_to_add_association 'Add Selection Option', f, :selections, class: "btn btn-primary btn-small"
I assume the has_one association you're referring to is the has_one :setting in MenuItem. If so, you might try adding update_only: true to your accepts_nested_attributes_for :setting options. From the documentation (emphasis mine):
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include the record's :id value. Otherwise a new record will be instantiated and used to replace the existing one. However if the :update_only option is true, the nested attributes are used to update the record’s attributes always, regardless of whether the :id is present.
This question is old, but this could help some people:
The reason the association was always recreated for me, was that I had forgotten to include :id in the permitted parameters of phone_number_attributes! If you forget to do that, rails wont get the id and will recreate a new record to replace the old one.
def user_params
params.require(:user).permit(
:avatar,
:remove_avatar,
{ id_photo_attributes: [:photo, :remove_photo] },
{ phone_number_attributes: [:id, :phone_number] } # dont forget :id here!
)
end

Activeadmin formtastic dynamic select

I would like to make a dynamic select option via Activeadmin's formtastic like so:
form do |f|
f.inputs "Exam Registration Details" do
f.input :user_id, :as => :select, :collection => User.where(:admin => 'false')
#selects user from list. WORKING
f.input :student_id, :as => :select, :collection => Student.joins(lessons: :user)
#collection of students will change to students who have lessons with chosen user. NOT WORKING, returns all students who have lessons.
f.input :lesson_id, :as => :select, :collection => Lesson.joins(:student, :user)
#collection of lessons will change to reflect lessons connected by chosen user and student. NOT WORKING, returns all lessons.
end
f.buttons
end
My amateurish code is obviously not working as I intended it to. What changes should I make?
I have 4 models as below:
class Student < ActiveRecord::Base
has_many :lessons
has_many :users, through: :lessons
has_many :exam_registrations, through: :lessons
class Lesson < ActiveRecord::Base
belongs_to :user
belongs_to :student
belongs_to :exam_registration
class User < ActiveRecord::Base
has_many :lessons
has_many :students, through: :lessons
has_many :exam_registrations, through: :lessons
class ExamRegistration < ActiveRecord::Base
has_many :lessons
has_many :users, through: :lessons
has_many :students, through: :lessons
SOLVED
For anyone else wrestling with the same problem, look at this railscast
here's how I implemented multiple dynamic select menus in activeadmin:
config/initializers/active_admin.rb
config.register_javascript 'exam_registrations.js.coffee'
app/admin/exam_registrations.rb
form do |f|
f.inputs "Exam Registration Details" do
f.input :user_id, :label => 'Teacher', :as => :select, :collection => User.where(:admin => 'false', :active => true).order(:name), :include_blank => true
f.input :student_id, :hint => 'Students grouped by teacher names', :as => :select, :collection => option_groups_from_collection_for_select(User.where(:admin => false, :active => true).order(:name), :students, :name, :id, :name)
f.input :lesson_id, :hint => 'Lessons grouped by student names', :as => :select, :collection => option_groups_from_collection_for_select(Student.where(:active => true).order(:name), :lessons, :name, :id, :name)
end
f.buttons
end
app/assets/javascripts/exam_registrations.js.coffee
#first menu
jQuery ->
$('#exam_registration_student_id').parent().hide()
students = $('#exam_registration_student_id').html()
$('#exam_registration_user_id').change ->
user = $('#exam_registration_user_id :selected').text()
escaped_user = user.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/#])/g, '\\$1')
options = $(students).filter("optgroup[label='#{escaped_user}']").html()
if options
$('#exam_registration_student_id').html(options)
$('#exam_registration_student_id').parent().show()
else
$('#exam_registration_student_id').empty()
$('#exam_registration_lesson_id').empty()
# second menu
$('#exam_registration_lesson_id').parent().hide()
lessons = $('#exam_registration_lesson_id').html()
$('#exam_registration_student_id').click ->
student = $('#exam_registration_student_id :selected').text()
escaped_student = student.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/#])/g, '\\$1')
options = $(lessons).filter("optgroup[label='#{escaped_student}']").html()
if options
$('#exam_registration_lesson_id').html(options)
$('#exam_registration_lesson_id').parent().show()
else
$('#exam_registration_lesson_id').empty()
restart the server and the menus work!

fields_for with through relation

Item gets the collection_fields from his collections.
For each collection_field of the collection item may have a field_value
models
class Item < ActiveRecord::Base
belongs_to :collection
has_many :field_values, :dependent => :destroy
has_many :collection_fields, :through => :collection
accepts_nested_attributes_for :field_values, :allow_destroy => true
end
class Collection < ActiveRecord::Base
has_many :items, :dependent => :destroy
has_many :collection_fields, :dependent => :destroy
end
class CollectionField < ActiveRecord::Base
belongs_to :collection
belongs_to :field
has_many :items, :through => :collection
has_many :field_values, :dependent => :destroy
end
class Field < ActiveRecord::Base
has_many :collection_fields
end
class FieldValue < ActiveRecord::Base
belongs_to :item
belongs_to :collection_field
end
controller
def new
#item = Item.new
#item.collection = Collection.find(params[:collection])
#item.collection.collection_fields.each do |cf|
#item.collection_fields << cf
end
def edit
#item = Item.find(params[:id])
view
<%= form_for(#item, :html => { :multipart => true }) do |f| %>
<% #item.collection_fields.each do |cf| %>
<% f.label cf.field.name %>
<%= f.fields_for :field_values, cf.field_values.find_or_create_by_item_id(#item.id) do |fv| %>
<%= fv.text_field :valore %>
This code is working fine with the edit method, but when I try to add a new item I get:
Couldn't find FieldValue with ID=213 for Item with ID=
How should I implement these form fields correctly?
I've finally worked out a solution. It's not so elegant, but it works
- #collection.collection_fields.each do |cf|
= f.label cf.field.name
- if #item.new_record?
= f.fields_for :field_values, #item.field_values.build() do |field_value|
= field_value.text_field :valore
= field_value.hidden_field :collection_field_id, :value => cf.id
- else
= f.fields_for :field_values, #item.field_values.find_or_create_by_collection_field_id(cf.id) do |field_value|
%td= field_value.text_field :valore

Rails simple_form association with nested form

My application has 3 models : consultant, project and appointment
I am using a nested form with simple_form gem
class Consultant < ActiveRecord::Base
has_many :appointments
end
class Project < ActiveRecord::Base
has_many :appointments, :dependent => :destroy
accepts_nested_attributes_for :appointments, :allow_destroy => true
end
class Appointment < ActiveRecord::Base
belongs_to :consultant
belongs_to :project
end
My form is as follows :
= simple_nested_form_for(#project) do |f|
%div.field
= f.input :name, :label => 'Nom du projet'
= f.simple_fields_for :appointments do |builder|
= render 'appointment_fields', :f => builder
= f.link_to_add "ajouter un consultant", :appointments
%div
%div.actions
= f.submit
with the partial :
%p.fields
= f.input :consultant_id, :input_html => { :class => 'special' }
= f.association :consultant
= f.input :nb_days, :input_html => { :class => 'special',:size => 10 }
= f.input :rate, :input_html => {:size => 10}
= f.link_to_remove "Remove this task"
Is it possible to do something as simple as this with simple_form ?
The answer is YES : it works nicely
The errors is because there is no association called consultant on the model Appointment. Use consultants instead.

Resources