Rails 4 nested form - nested models not saving - ruby-on-rails-4

I am getting ready to give up on this... I 've searched every question on this, done multiple tests and I can't figure it out.
I am trying to create new records of a model (absences) through nested attributes (classroom -> students).
Here is a hash that I manually created and gets saved when I call
# hash: {"classroom"=>{"rank"=>"aaa", "grade_id"=>"", "students_attributes"=>{"0"=>{"last_name"=>"aaa", "first_name"=>"aaa", "absences_attributes"=>{"0"=>{"class_time"=>"1", "status"=>"valid"}}}}}, "id"=>"26"}
#classroom = Classroom.find(params["id"])
#classroom.update(params["classroom"])
This gives me:
(0.5ms) begin transaction
SQL (1.3ms) UPDATE "classrooms" SET "rank" = ?, "updated_at" = ? WHERE "classrooms"."id" = ? [["rank", "aaa"], ["updated_at", "2016-08-10 21:09:17.883875"], ["id", 26]]
SQL (0.2ms) INSERT INTO "students" ("last_name", "first_name", "classroom_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["last_name", "aaa"], ["first_name", "aaa"], ["classroom_id", 26], ["created_at", "2016-08-10 21:09:17.897997"], ["updated_at", "2016-08-10 21:09:17.897997"]]
SQL (0.2ms) INSERT INTO "absences" ("class_time", "status", "student_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["class_time", 1], ["status", "Αδικαιολόγητη"], ["student_id", 51], ["created_at", "2016-08-10 21:09:17.902018"], ["updated_at", "2016-08-10 21:09:17.902018"]]
(11.4ms) commit transaction
=> true
HOWEVER
when I get this hash through the form submission the update doesn't happen and no "absences" are getting created. Here is what the console looks like:
Processing by ClassroomsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"blabla",
"classroom"=>{"students_attributes"=>{"0"=>{"absences_attributes"=>{
"0"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"1", "status"=>"valid"},
"1"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"2", "status"=>"valid"},
"2"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"3", "status"=>"valid"},
"3"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"4", "status"=>"valid"},
"4"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"5", "status"=>"valid"},
"5"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"6", "status"=>"valid"},
"6"=>{"student_id"=>"50", "date"=>"2016/08/17", "class_time"=>"7", "status"=>"valid"}},
"id"=>"50"}}}, "id"=>"27"}
Classroom Load (0.2ms) SELECT "classrooms".* FROM "classrooms" WHERE "classrooms"."id" = ? LIMIT 1 [["id", 27]]
(0.1ms) begin transaction Student Load
(0.3ms) SELECT "students".* FROM "students" WHERE "students"."classroom_id" = ? AND "students"."id" = 50 [["classroom_id", 27]]
(0.1ms) commit transaction
Here is my Classrooms Controller
class ClassroomsController < ApplicationController
def new
#classroom = Classroom.new
#students = 30.times {#classroom.students.build}
end
def create
#classroom = Classroom.new(classroom_params)
if #classroom.save
redirect_to #classroom
else
flash.now[:danger] = "Try again."
render "new"
end
end
def show
#classroom = Classroom.find(params[:id])
end
def data
#classroom = Classroom.find(params[:id])
#classroom.students.each { |student| 7.times { student.absences.build } }
end
def update
#classroom = Classroom.find(params[:id])
#classroom.update_attributes(classroom_params)
redirect_to #classroom
end
private
def classroom_params
params.require(:classroom).permit(:id, :grade_id, :rank, students_attributes:
[:id, :classroom_id, :first_name, :last_name, absences_attributes:
[:id, :student_id, :date, :status, :class_time]])
end
end
Classroom Model
class Classroom < ActiveRecord::Base
# belongs_to :grade
has_many :students, inverse_of: :classroom, dependent: :destroy
accepts_nested_attributes_for :students, allow_destroy: true,
reject_if: proc { |a| [a[:first_name], a[:last_name]].any? {|b| b.blank?}}
has_many :absences, through: :students
accepts_nested_attributes_for :absences, allow_destroy: true,
reject_if: proc { |a| a[:status].blank? }
validates :rank, presence: true
end
Student Model
class Student < ActiveRecord::Base
belongs_to :classroom, inverse_of: :students
has_many :absences, inverse_of: :student
accepts_nested_attributes_for :absences, allow_destroy: true,
reject_if: proc { |a| a[:status].blank? }
# validates :first_name, presence: true
# validates :last_name, presence: true
def full_name
return last_name + " " + first_name
end
end
Absence Model
class Absence < ActiveRecord::Base
belongs_to :student, inverse_of: :absences
# validates :date, presence: true
# validates :class_time, presence: true
# validates :status, presence: true, inclusion: { in: ["valid",
# "invalid", "erased"] }
end
I have turned off all validations as to be sure this is not the reason they are not being saved.
Nested form view
<div id="absences" class="tab-pane fade in active">
<h3>Absences</h3>
<br>
<div class="input-group date col-md-3" id="datepicker">
<input type="text" class="form-control">
<div class="input-group-addon">
<span class="glyphicon glyphicon-th"></span>
</div>
</div>
<%= form_for #classroom do |class_f| %>
<% #classroom.students.each do |student| %>
<div class="student_<%= student.id %>">
<h4><%= student.full_name %></h4>
<div class="absences">
<p>
<% 7.times do |i| %>
<span><%= i + 1 %></span>
<% end %>
</p>
<%= class_f.fields_for :students, student do |student_f| %>
<% student.absences.each_with_index do |absence, i| %>
<div class="school-hour text-center" data-value="<%= i + 1 %>">
<%= fa_icon "plus" %>
<%= fa_stacked_icon "plus", base: "circle-o" %>
<%= fa_icon "minus" %>
<%= student_f.fields_for :absences, absence do |ab_f| %>
<%= ab_f.hidden_field :student_id, value: absence.student_id %>
<%= ab_f.hidden_field :date, class:"input-absence-date" %>
<%= ab_f.hidden_field :class_time, value: i + 1 %>
<%= ab_f.hidden_field :status, class:"input-absence-status" %>
<% end %>
</div>
<% end %>
<% end %>
</div>
</div>
<% end %>
<div class="actions">
<%= button_to "Submit", "#", class:"btn btn-primary"%>
</div>
<% end %>
</div>
The hidden fields without values are getting filled via javascript before form submission. I don't think that relates to the problem though. The params hash comes through, but the absences are not saving.

Without having spent a lot of time looking at this, could this be the problem?
reject_if: proc { |a| a[:status].blank? }
It appears the params are passing in status as "" which .blank? evaluates as true.
Whatever your form is passing in, in your first example you have "status"=>"valid" but in your 2nd example from the form "status"=>""

I found out that the problem was the reject_if on the classroom model.
I was trying to update the absence through the user, I wasn't actually changing the name of the user, and so not passing it as a param. As a result the record didn't pass the reject_if: proc { |a| [a[:first_name], a[:last_name]].any? {|b| b.blank?}} so it didn't reach the absence record at all. I tried putting a :new_record? validation in the reject if along with the other checks, but that didn't work out.
I found the solution in this answer: https://stackoverflow.com/a/13774269/5909738
Awesome and clean. Look at the comments as well.

Related

Ruby on Rails 4: Searchkick displaying all items instead of searched results

I have a Forum that has_many :forum_threads and has_many :forum_posts, through: :forum_threads. So basically my Forums have their own Forum Threads that are related to that particular Forum topic. I've added Searchkick to handle the search form. Searchkick is working for my Forum table but not for my ForumThread table.
There is no error, it just renders out all of the forum threads instead of rendering the query that the form was given.
Searchkick GitHub
Here are my files:
forum.rb
class Forum < ActiveRecord::Base
accepts_nested_attributes_for :forum_threads
has_many :forum_posts, through: :forum_threads
accepts_nested_attributes_for :forum_posts
searchkick text_start: [:title], suggest: [:title], autocomplete: [:title]
def search_data
{
title: title
}
end
end
forums_controller.rb
class ForumsController < ApplicationController
def index
query = params[:q] || "*"
#forums = Forum.search(query, suggest: true, fields: [:title],
boost_where: {specific: :exact})
end
end
views/forums/index.html.erb
<%= form_tag forums_path, method: :get do |f| %>
<%= text_field_tag :q, nil, class: 'form-control', placeholder: 'Search...' %>
<% end %>
<% if #forums.suggestions.any? %>
<p class="lead">
<em>Did you mean: <strong><%= #forums.suggestions.first %></strong>?</em>
</p>
<% end %>
forum_thread.rb
class ForumThread < ActiveRecord::Base
belongs_to :user, counter_cache: true
belongs_to :forum, counter_cache: true, touch: true
has_many :forum_posts, dependent: :destroy
accepts_nested_attributes_for :forum_posts
validates :subject, presence: true
validates_associated :forum_posts
searchkick text_start: [:subject], suggest: [:subject], autocomplete: [:subject]
def search_data
{
subject: subject,
description: description
}
end
end
forum_threads_controller.rb
class ForumsController < ApplicationController
def index
query = params[:q].presence || "*"
#forum_threads = #forum.forum_threads.search(query, suggest: true, fields: [:subject, :description])
end
end
views/forum_threads/index.html.erb
<%= form_tag forum_forum_threads_path(<!-- something here? -->), method: :get do %>
<%= text_field_tag :q, nil, class: 'form-control', placeholder: 'Search...' %>
<% end %>
<% if #forum_threads.suggestions.any? %>
<p class="lead"><em>Did you mean: <%= #forum_threads.suggestions.first %>?</em></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :forums do
resources :forum_threads do
resources :forum_posts do
member do
put 'like', to: 'forum_posts#upvote'
put 'dislike', to: 'forum_posts#downvote'
end
end
end
end
end
I just made a variable for suggestions to use in the view.
forum_threads_controller.rb
def index
query = params[:q].presence || "*"
#forum_threads = ForumThread.search(query,
where: { forum_id: #forum.id },
fields: [:subject, :description],
boost_where: {specific: :exact}, suggest: true, highlight: true)
#forum_threads_suggestions = []
if #forum_threads.empty? && #forum_threads.suggestions.any?
#forum_threads_suggestions = ForumThread.search(#forum_threads.suggestions.first, fields: [:subject], suggest: true,
boost_where: {specific: :exact}, highlight: true)
end
end
index.html.erb
<%= form_tag forum_forum_threads_path, method: :get do %>
<%= text_field_tag :q, nil, class: 'form-control', placeholder: 'Search...' %>
<% end %>
<% if #forum_threads.suggestions.any? %>
<p class="lead">
<em>
Did you mean: <strong><%= #forum_threads.suggestions.first %></strong>?
</em>
</p>
<% end %>
</div>
<!-- END SEARCH FORM -->
<!-- SHOW RESULTS AND SUGGESTIONS -->
<p class="light-grey"><em>
<%= pluralize #forum_threads.results.size, 'match' if params[:q].present? %>
<%= pluralize #forum_threads.suggestions.size, 'suggestion' if #forum_threads.suggestions.any? %>
</em></p>
<!-- END RESULTS AND SUGGESTIONS -->
<!-- SHOW FORUM THREADS -->
<% (#forum_threads.presence || #forum_threads_suggestions).each do |forum_thread| %>
I wrote an or statement to either show all forum_threads or suggestions.
forum_thread.rb)
searchkick fields: [:subject], suggest: ['subject'], highlight: [:subject]
def search_data
{
forum_id: forum.id,
forum_thread: #forum_thread,
subject: subject
}
end
I did the same thing for the forum model. I'm not sure if this is the best way to go about doing this so if someone else has something then please feel free to contribute.

Act-As-Taggable not saving to database

Hi I am attempting to use act_as_taggable ruby gem and have followed the github guide and a recent tutorial as well as checked stack overflow for advice. My code all seems to be correct but when I attempt to save tags via my form partial or via the rails console they do not save to the database.
Below relevant code excerpts.
Thanks in advance for any help!
post.rb
class Post < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :tags
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
extend FriendlyId
friendly_id :title, use: :slugged
end
post_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:tag].present?
#posts = Post.tagged_with(params[:tag]).paginate(:page => params[:page], :per_page => 10)
else
#posts = Post.all.order('created_at DESC').paginate(page: params[:page], per_page: 10)
end
end
def new
#post = Post.new
end
def show
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post, notice: "Article succesfully saved!"
else
render 'new', notice: "Try Again. I was unable to save your post."
end
end
def edit
end
def update
if #post.update(params[:post].permit(:title, :body))
redirect_to #post, notice: "Article succesfully edited!"
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body, :slug, :tag_list => [])
end
def find_post
#post = Post.friendly.find(params[:id])
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :skip => :registrations
root 'pages#home'
resources :posts
resources :portfolios
get 'tags/:tag', to: 'posts#index', as: :tag
end
_form.html.erb
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title, class: 'form-control form-control-lg' %><br>
<br>
<%= f.label :body %><br>
<%= f.text_area :body, class: 'form-control form-control-lg', :rows => "10" %><br>
<br>
<%= f.label :tag_list, "Tags (separated by commas)" %>
<%= f.text_field :tag_list, class: 'form-control' %>
<br>
<%= f.submit %>
<% end %>
excerpt from show.html.erb
<% #post.tags.any? %>
<% #post.tags.each do |tag| %>
<li><a href="#">
<%= link_to tag.name, tag_path(tag.name) %>
</a></li>
<% end %>
Params log for _form as requested by Pavan:
Started PATCH "/posts/my-third-post-with-tags" for ::1 at 2015-09-23 15:56:36 +1000
Processing by PostsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"GwLMImKSDyhPt77nI+fhIkF4QVsFxMen851wjcVorrxOCdU+TcRKIZv1im4/oNx7DERYCI91vE9YDjvSda7MAg==", "post"=>{"title"=>"My third post with tags", "body"=>"to update your local repository to the newest commit, execute\r\ngit pull\r\nin your working directory to fetch and merge remote changes.\r\nto merge another branch into your active branch (e.g. master), use\r\ngit merge <branch>\r\nin both cases git tries to auto-merge changes. Unfortunately, this is not always possible and results in conflicts. You are responsible to merge those conflicts manually by editing the files shown by git. After changing, you need to mark them as merged with\r\ngit add <filename>\r\nbefore merging changes, you can also preview them by using\r\ngit diff <source_branch> <target_branch>", "tag_list"=>"tag, css"}, "commit"=>"Update Post", "id"=>"my-third-post-with-tags"}
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = ? ORDER BY "posts"."id" ASC LIMIT 1 [["slug", "my-third-post-with-tags"]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Unpermitted parameter: tag_list
(0.0ms) begin transaction
(0.0ms) commit transaction
Redirected to http://localhost:3000/posts/my-third-post-with-tags
Completed 302 Found in 4ms (ActiveRecord: 0.3ms)
Started GET "/posts/my-third-post-with-tags" for ::1 at 2015-09-23 15:56:36 +1000
Processing by PostsController#show as HTML
Parameters: {"id"=>"my-third-post-with-tags"}
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = ? ORDER BY "posts"."id" ASC LIMIT 1 [["slug", "my-third-post-with-tags"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? AND "taggings"."context" = ? LIMIT 1 [["taggable_id", 2], ["taggable_type", "Post"], ["context", "tags"]]
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? AND "taggings"."context" = ? [["taggable_id", 2], ["taggable_type", "Post"], ["context", "tags"]]
Rendered posts/show.html.erb within layouts/application (3.2ms)
Rendered layouts/_header.html.erb (496.3ms)
Rendered devise/shared/_links.html.erb (0.2ms)
Rendered devise/sessions/_new.html.erb (4.6ms)
Rendered layouts/_slideoutpanels.html.erb (460.2ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 1486ms (Views: 1484.3ms | ActiveRecord: 0.2ms)
Your error is from PostsController#update as HTML
In controller you don;t permit tag_list
#post.update(params[:post].permit(:title, :body))

Not sure why the VIEW does't work: Rails 4 nested attributes and has_many :through associaton in a form

I followed this page to build my app:
Rails 4 nested attributes and has_many :through associaton in a form
but it shows NOTHING in my VIEW:
(the weird thing is when i typed "f.fields_for :questionnaire_surveRys do |ff|" instead of the right one, it showed me the ocrrect page.
any suggestions will be greatly appreciated.
here are my Models:
questionnaire.rb
class Questionnaire < ActiveRecord::Base
has_many :questionnaire_surveys
has_many :surveys, through: :questionnaire_surveys
accepts_nested_attributes_for :questionnaire_surveys
end
questionnaire_survey.rb
class QuestionnaireSurvey < ActiveRecord::Base
belongs_to :questionnaire
belongs_to :survey
accepts_nested_attributes_for :survey
end
survey.rb
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
has_many :questionnaire_surveys
has_many :questionnaires, through: :questionnaire_surveys
end
and this is my questionnaire_controller.rb
def new
#questionnaire = Questionnaire.new
#surveys = Survey.all
end
def questionnaire_params
params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:id, survey_attributes:[:id]])
end
this is my _form.html.erb
<%= form_for(#questionnaire) do |f| %>
<p>
<%= f.label :name %><br/>
<%= f.text_field :name %>
<div class="field">
<%= f.fields_for :questionnaire_surveys do |ff| %>
<%= ff.fields_for :survey do |builder| %>
<% #surveys.each do |survey| %>
<%= builder.check_box :id, {}, survey.id %>
<%= builder.label survey.name %>
<% end %>
<% end %>
<% end %>
</div>
</p>
<div class="actions">
<%= f.submit %>
</div>
UPDATED:
Started POST "/questionnaires" for ::1 at 2015-07-29 22:45:16 +0800
Processing by QuestionnairesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"k4SkRC08PwAHAo1iERmQCkssdQZYgf+uHwofPdeLbXo0O4/psY3Y7i/krQA01omToQ4VLlt/YQDNkcbpLGp86w==", "questionnaire"=>{"name"=>"what just happened", "questionnaire_surveys_attributes"=>{"0"=>{"survey_attributes"=>{"name"=>""}}}}, "commit"=>"Create Questionnaire"}
Unpermitted parameter: name
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "questionnaires" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "what just happened"], ["created_at", "2015-07-29 14:45:16.374246"], ["updated_at", "2015-07-29 14:45:16.374246"]]
SQL (0.2ms) INSERT INTO "surveys" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-07-29 14:45:16.377439"], ["updated_at", "2015-07-29 14:45:16.377439"]]
SQL (0.1ms) INSERT INTO "questionnaire_surveys" ("questionnaire_id", "survey_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["questionnaire_id", "52"], ["survey_id", "38"], ["created_at", "2015-07-29 14:45:16.378845"], ["updated_at", "2015-07-29 14:45:16.378845"]]
(0.9ms) commit transaction
Redirected to http://localhost:3000/questionnaires/52
Completed 302 Found in 12ms (ActiveRecord: 2.0ms)
UPDATE - 2015/7/31
Started POST "/questionnaires" for ::1 at 2015-07-31 17:46:50 +0800
Processing by QuestionnairesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"t/00prIClAUVdqPFxOnkTaxRPhTdY082PAvHb/VQSO4QQh8LLrNz6z2Qg6fhJv3URnNePN6d0ZjukB67DrFZfw==", "questionnaire"=>{"name"=>"OMG", "questionnaire_surveys_attributes"=>{"0"=>{"survey_attributes"=>{"name"=>""}}}}, "commit"=>"Create Questionnaire"}
(0.2ms) begin transaction
SQL (0.7ms) INSERT INTO "questionnaires" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "OMG"], ["created_at", "2015-07-31 09:46:50.440466"], ["updated_at", "2015-07-31 09:46:50.440466"]]
SQL (0.4ms) INSERT INTO "surveys" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", ""], ["created_at", "2015-07-31 09:46:50.446176"], ["updated_at", "2015-07-31 09:46:50.446176"]]
SQL (0.2ms) INSERT INTO "questionnaire_surveys" ("questionnaire_id", "survey_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["questionnaire_id", "53"], ["survey_id", "39"], ["created_at", "2015-07-31 09:46:50.450001"], ["updated_at", "2015-07-31 09:46:50.450001"]]
(0.9ms) commit transaction
Redirected to http://localhost:3000/questionnaires/53
Completed 302 Found in 22ms (ActiveRecord: 2.4ms)
UPDATE - 2015/8/05
I can't upload pics here, hope this is what you need:
<input placeholder="vision" type="text" name="questionnaire[questionnaire_surveys_attributes][0][survey_attributes][name]" id="questionnaire_questionnaire_surveys_attributes_0_survey_attributes_name">
UPDATE - 2015/8/11
_form.erb.html
<div class="field">
<% #surveys.each do |survey| %>
<%= check_box_tag "questionnaire[questionnaire_surveys_attributes][][survey_id]", survey.id %>
<%= label_tag survey.name %>
<% end %>
</div>
questionnaires_controller.rb
params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:survey_id])
def new
#questionnaire = Questionnaire.new
#surveys = Survey.all
end
UPDATE - 2015/8/17
I misused the has_many :through and accepts_nested_attributes_for.
In has_many:xxx :through case, there is xxx_ids.
In accepts_nested_attributes_for xxx case, there is xxx_attributes.
I used accepts_nested_attributes_for in both questionnaire.rband questionnaire_survey.rb , which is a mistake.
The correct way to do what I want is use the has_many :through only.
Then my questionnaire_controller.rb will have
def questionnaire_params
params.require(:questionnaire).permit(:name, :survey_id=>[])
end
in _form view, it should be
<%= check_box_tag "questionnaire[survey_id][]", survey.id %>
it's much easier now.
#Rich Peck thanks for all your help.
First things first - if you're not seeing the form elements appear, it's because you've not got it set up correctly in the backend.
For the longest time, I tried to set this up and was getting very frustrated that the embedded form would not appear. It wasn't until I sorted it out properly that it worked. It's called graceful degradation (I think) - whereby no error will appear, yet functionality will be impaired.
Firstly, I think you haven't built your associated objects in the controller:
#app/controllers/questionnaire_controller.rb
def new
#questionnaire = Questionnaire.new
# You need to build the associated objects, like this:
#questionnaire.questionnaire_surveys.build.build_survey
#surveys = Survey.all
end
--
Secondly, there is a better way to show checkboxes for your #surveys object:
<%= ff.fields_for :survey do |survey| %>
<%= survey.collection_check_boxes :survey_ids, #surveys, :id, :name %>
<% end %>
You can read up about collection_check_boxes here
--
Thirdly, you should definitely learn haml. You could write your entire form like this:
= form_for #questionnaire do |f|
.name
= f.label :name
= f.text_field :name
.field
= f.fields_for :questionnaire_surveys do |ff| %>
= ff.fields_for :survey do |survey| %>
= survey.collection_check_boxes :survey_ids, #surveys, :id, :name
.actions
= f.submit
--
Finally, don't use HTML elements as styling.
<p> & <br> should only be used as markup. If you're using them for styling effect, you'll end up causing problems with browser compatibility etc.
You need to let your CSS do the styling (colouring, size, position), and any on-page elements used as ways to separate the content of your application.
Update
Okay, so I've looked at your BitBucket:
You need to uncomment #questionnaire.questionnaire_surveys.build.build_survey in app/controllers/questionnaires_controller.rb#20
If you do that, it should work.
I cannot see any problems with the construct of the models and controllers. Are you sure you've refreshed etc?
I see you're calling <%= render "form" %> - try putting the form directly in the new view to test if it will work.
Also, have you tried using a simple way to add the extra fields, like this:
<%= f.fields_for :questionnaire_surveys do |ff| %>
<%= ff.fields_for :survey do |builder| %>
<% #surveys.each do |survey| %>
<%= builder.text_field :name, placeholder: survey.name %>
<% end %>
<% end %>
Finally, if you post your posted parameters after form submit, I'll be in a much stronger position to see any of the errors/problems you may have.
--
You can change your params to the following:
#app/controllers/questionnaires_controller.rb
...
def questionnaire_params
params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:id, survey_attributes:[:name]])
end

saving empty records via form_for - wrong number of arguments

Update: Zishe figured it out. Correct params.require code should be:
def adventure_params
params.require(:adventure).permit(:story, :choice, :parent_id, :user_id)
end
with parenthesis instead of a bracket, of course.
Original question:
I am making a form_for that should be submitting 4 attributes to the Adventure model. The adventure model has ancestry. I keep getting wrong number of arguments (4 for 1) based on my params.require method. When I change to requirements down to 1, I see that all the attributes are blank in my database. I know they are in the params but for some reason they are not being saved. Here is my code:
Form
<div class="form">
<%= form_for #adventure do |f| %>
<%= f.label :choice %>
<%= f.text_area :choice %>
<%= f.label :story %>
<%= f.text_area :story %>
<%= f.label :parent_id %>
<%= f.text_field :parent_id %>
<%= f.submit "Post"%>
<% end %>
</div>
Controller
class AdventuresController < ApplicationController
before_filter :authenticate_user!
def home
end
def index
end
def new
#parent_id = params[:parent_id]
#adventure = Adventure.new
end
def show
#adventure = Adventure.find(params[:id])
end
def create
#user = current_user
#adventure = current_user.adventures.build(adventure_params)
if #adventure.save
flash[:success] = "Adventure created!"
redirect_to #adventure
else
flash[:error] = "There was an error"
redirect_to adventures_path
end
end
private
def adventure_params
params.require(:adventure).permit[:story, :choice, :parent_id, :user_id]
end
end
Model
class Adventure < ActiveRecord::Base
has_ancestry
belongs_to :user
validates :user_id, presence: true
validates :parent_id, presence: true
end
I have no idea why I am getting wrong number of arguments since the attributes show up in the params.
Change permit[...] to:
.permit(:story, :choice, :parent_id, :user_id)

Strong Params won't save nested has_many

I'm using Rails 4, and with it Strong Params. For some reason I can't save my nested parameters. I've looked at how to do it and even might've gotten it working in a different Rails app before. I know to permit draft_players_attributes and list all of it's accepted params in an array.
Here are the params coming in:
Started POST "/draft_groups" for 127.0.0.1 at 2013-12-04 22:55:32 -0500
User Load (1.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Processing by DraftGroupsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2W3bs1U7+CEzsWl+jDi3xZi5CyldYeZXCz3KU6c+sYY=", "draft_group"=>{"name"=>"Best Group Ever", "draft_id"=>"3", "captain_id"=>"1"}, "draft_players"=>{"player_id"=>"1", "position"=>"Handler", "rating"=>"10", "info"=>"Smart"}, "commit"=>"Update"}
(0.3ms) BEGIN
SQL (2.6ms) INSERT INTO "draft_groups" ("captain_id", "created_at", "draft_id", "name", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["captain_id", 1], ["created_at", Thu, 05 Dec 2013 03:55:32 UTC +00:00], ["draft_id", 3], ["name", "Best Group Ever"], ["updated_at", Thu, 05 Dec 2013 03:55:32 UTC +00:00]]
(0.5ms) COMMIT
Redirected to http://lvh.me:3000/drafts/3
Completed 302 Found in 8ms (ActiveRecord: 3.3ms)
Here's my controller:
class DraftGroupsController < ApplicationController
def create
#draft_group = DraftGroup.create(draft_group_params)
redirect_to :back, :notice => "Draft Group successfully created."
end
def update
#draft_group = DraftGroup.find(params[:id])
#draft_group.update(draft_group_params)
redirect_to :back, :notice => "Draft Group successfully updated."
end
def destroy
#draft_group = DraftGroup.find(params[:id]).destroy
redirect_to :back, :notice => "Draft Group successfully destroyed."
end
private
def draft_group_params
params.require(:draft_group).permit(:name,
:draft_id,
:captain_id,
draft_players_attributes:
[
:_destroy,
:id,
:player_id,
:position,
:rating,
:info
]
)
end
end
And my models:
class DraftGroup < ActiveRecord::Base
has_many :draft_players, :dependent => :destroy
belongs_to :captain, :class_name => "User"
accepts_nested_attributes_for :draft_players
end
class DraftPlayer < ActiveRecord::Base
belongs_to :draft_group
belongs_to :player, class_name: "User"
end
And my view:
<% #groups.each do |group| %>
<div class="span6 group">
<h4><%= "#{group.name}" %></h4>
<%= simple_form_for(group, :html => { :class => "auto-width" } ) do |f| %>
<div class="row">
<%= f.input :name, :label => false %>
<%= f.hidden_field :draft_id %>
<%= f.hidden_field :captain_id %>
</div>
<table>
<tr>
<th>Player</th>
<th>Position</th>
<th>Rating</th>
<th>Info</th>
</tr>
<%= simple_fields_for :draft_players do |player| %>
<tr>
<td><%= player.input :player_id, :label => false, :as => :select, :collection => User.active %></td>
<td><%= player.input :position, :label => false %></td>
<td><%= player.input :rating, :label => false %></td>
<td><%= player.input :info, :label => false %></td>
</tr>
<% end %>
</table>
<div class="row">
<%= f.button :submit, "Update", :class => "btn btn-primary" %>
</div>
<% end %>
</div>
<% end %>
EDIT: Added view code, and will probably take table out, shifting it into using bootstraps columns layout.
To make sure parameters are nested correctly (and so Rails can understand nested model attributes) you should call f.simple_fields_for(:draft_players) rather than simple_fields_for(:draft_players).
In other words, call simple_fields_for on the FormBuilder object f rather than calling the helper directly.
That way Rails can look up and validate the nested association correctly.