Nested attributes not being associated

Viewed 20

I am having some unexpected behaviour with nested attributes in that the nested attributes are saving only the default values defined in the model. I have read a lot of documentation and tried many configurations of the build_ method prefixes in the controller, but this is the best I have come up with so far. Seem like the data is being persisted to the DB, however only the default values, so I suspect the issue lies with the form or the build method in controller. I thought the foreign keys were possibly wrong, although I think that's not the issue any more. Newish to rails (please forgive). I have read many questions and answers on related issues, but none have done the trick. Thanks for any tips and pointers.

I have 3 models, user, year and months. All belonging to each other in the following fashion:

class User < ApplicationRecord
   devise :database_authenticatable, :registerable,
   :recoverable, :rememberable, :validatable

  has_many :years, dependent: :destroy
end

class Year < ApplicationRecord
  belongs_to :user

  has_one :month, dependent: :destroy, inverse_of: :year

  validates :year, presence: true
  accepts_nested_attributes_for :month
end

class Month < ApplicationRecord
  belongs_to :year, inverse_of: :month
  attribute :january, :integer, default: 110
  attribute :febuary, :integer, default: 0
  ....etc
end

The years controller is like this

def new
  @year = current_user.years.build
end
def create
  @year = current_user.years.build(year_params)
...etc...
end

def year_params
    params.require(:year).permit(:year, :monthly_target, :user_id, 
      months_attributes: [:january, :febuary, :march, :april, :may, :june, :july, :august, 
      :september, :october, :november, :december])
end

The form is like this:

  <div>
      <%= form.label :year, style: "display: block" %>
      <%= form.number_field :year, min:0, in: 1990...2051 %><span><small>add a year from 1990 
       to 2050</small></span>
  </div>

  <div>
    <%= form.label :monthly_target, style: "display: block" %>
    <%= form.number_field :monthly_target, min: 0, step: 25 %>
  </div>
  <%= form.fields_for :months do |month_form| %>
    <div>
      <%= month_form.label :january, style: "display: block" %>
      <%= month_form.number_field :january, min: 0, step: 25%>
    </div>

  <div>
    <%= month_form.label :febuary, style: "display: block" %>
    <%= month_form.number_field :febuary, min: 0, step: 25 %>
  </div>

Schema is like this:

ActiveRecord::Schema[7.0].define(version: 2022_09_20_123059) do
   create_table "months", force: :cascade do |t|
   t.integer "febuary"
   t.integer "march"
   t.integer "april"
   t.integer "may"
   t.integer "june"
   t.integer "july"
   t.integer "august"
   t.integer "september"
   t.integer "october"
   t.integer "november"
   t.integer "december"
   t.integer "year_id", null: false
   t.datetime "created_at", null: false
   t.datetime "updated_at", null: false
   t.index ["year_id"], name: "index_months_on_year_id"
end

create_table "users", force: :cascade do |t|
  t.string "email", default: "", null: false
  t.string "encrypted_password", default: "", null: false
  t.string "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["email"], name: "index_users_on_email", unique: true
  t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

create_table "years", force: :cascade do |t|
  t.integer "year"
  t.integer "monthly_target"
  t.integer "user_id", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["user_id"], name: "index_years_on_user_id"
end

  add_foreign_key "months", "years"
  add_foreign_key "years", "users"
end

ps: this is rails 7 on windows, not using WSL2 (yet...!)

edit: here are params being sent through>>

{"authenticity_token"=>"[FILTERED]",
 "year"=>
  {"year"=>"2022",
   "monthly_target"=>"25",
   "months"=>{"january"=>"", "febuary"=>"", "march"=>"", "april"=>"", 
      "may"=>"", "june"=>"", "july"=>"", "august"=>"", "september"=>"", 
       "october"=>"", "november"=>"50", "december"=>""}},
 "commit"=>"Create Year"}

also, here is the instance for the @year variable

>> 

@year

=> #<Year id: nil, year: 2022, monthly_target: 25, user_id: 1, 
created_at: nil, updated_at: nil>

you can see I added in the form 50 to november and the rest are empty fields. This is ok though as I opted for default values of 0 on all fields in the months model. I must say that I have found a decent work around, which I think is in the Rails ethos. I redesigned everything to just use one model "Year" and have added methods on the "Year" model to do the things I want, keeping the controller quite empty. This seems to work well, but I am slightly frustrated that I was unable to get the association between two models to work... Thanks for your help.

0 Answers
Related