0
votes

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.

2

2 Answers

1
votes

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"=>""

0
votes

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.