ChrisDekker
ChrisDekker

Reputation: 1763

Rails 5 Assignment of _ids array fails on has_many: through association

When upgrading from Rails 3.2 to Rails 5 I ran into this problem where a model suddenly refuses to save.

I have 2 models: ClosingDay and Location which have a has_many :through relation via a ClosingDayLocation model. When creating a ClosingDay the user needs to select 1 or more Location records using checkboxes.

However, regardless of which Location records I select on the form, I always get an error saving the ClosingDay record telling me that the associated ClosingDayLocation is invalid because closing_day_id is empty.

Models:

class ClosingDay < ActiveRecord::Base
  has_many :closing_day_locations, :dependent => :destroy
  has_many :locations, :through => :closing_day_locations
end

class Location < ActiveRecord::Base
  has_many :closing_day_locations, :dependent => :destroy
  has_many :closing_days, :through => :closing_day_locations
end

class ClosingDayLocation < ActiveRecord::Base
  belongs_to :location
  belongs_to :closing_day

  validates :closing_day_id, :presence => true
  validates :location_id, :presence => true
end

Controller

  def create
    @closing_day = ClosingDay.build(closing_day_params)

    if @closing_day.save
      redirect_to(closing_days_url, :notice => 'OK')
    else
      @locations = Location.active.order(:name)
      render :action => 'new'
    end
  end

  private
  def closing_day_params
    params.require(:closing_day).permit(:date, :name, location_ids: [])
  end

Form

<%= form_for(@closing_day) do |f| %>
    <%= show_errors_for(@closing_day) %>
    <table>
      <tr>
        <td><%= f.label :name %></td>
        <td><%= f.text_field :name %></td>
      </tr>
      <tr>
        <td><%= f.label :date %></td>
        <td><%= f.text_field :date %></td>
      </tr>
      <tr>
        <td><%= label_tag 'closing_day[location_ids][]', 'Locations' %></td>
        <td>
          <%= hidden_field_tag 'closing_day[location_ids][]', nil %>
          <% @locations.each do |location| %>
              <p>
                <%= label_tag do %>
                    <%= check_box_tag 'closing_day[location_ids][]', location.id, @closing_day.location_ids.include?(location.id) %>
                    <%= location.name %>
                <% end %>
              </p>
          <% end %>
        </td>
      </tr>
      <tr>
        <td colspan="2"><%= f.submit %></td>
      </tr>
    </table>
<% end %>

So in short, I want to create a new @closing_day record and assign associated (pre-existing) locations by assigning Location IDs using @closing_day.location_ids = [3,6,9] essentially. This works in Rails 3.2 but does not seem to work in Rails 5.

What is the best way to still keep this functionality? I have also tried adding accepts_nested_attributes_for :locations, :allow_destroy => true and accepts_nested_attributes_for :closing_day_locations, :allow_destroy => true but that does not seem to work. The associated ClosingDayLocation models seem to be created, but the closing_day_id remains empty, resulting in an error.

Upvotes: 1

Views: 1478

Answers (1)

ChrisDekker
ChrisDekker

Reputation: 1763

After some more tinkering I found the problem and solved it. I'll share it here in case others run into the same issues!

class ClosingDayLocation < ActiveRecord::Base
  belongs_to :location
  belongs_to :closing_day

  #validates :closing_day_id, :presence => true
  #validates :location_id, :presence => true
end

After removing those 2 validations on the intermediate model it worked fine for both creating and updating. Apparently some ordering in validating and saving associated records has shifted around between Rails 3.2 and Rails 5.

Upvotes: 1

Related Questions