Rails accepts_nested_attributes_for and a ternary association

94 Views Asked by At

tl, dr: Is it possible to populate a ternary association with accepts_nested_attributes and consequently pass the tests from this PR?

Basically I created four models A, B, C and Abc and the latter is a join table for a ternary association with the other models. The problem is using accepts_nested_attributes_for I can't seem to save the whole ternary association. Instead I either create two join models with binary associations (A-B, B-C or A-C) or the database complains about missing foreign keys. Checkout this test and the code:

class A < ApplicationRecord
  has_and_belongs_to_many :bs, join_table: :abcs
  has_and_belongs_to_many :cs, join_table: :abcs

  accepts_nested_attributes_for :bs, :cs
end

class B < ApplicationRecord; end
class C < ApplicationRecord; end

class Abc < ApplicationRecord
  belongs_to :a
  belongs_to :b
  belongs_to :c
end

class AsController < ApplicationController
  def new
    @a = A.new

    @a.bs.build
    @a.cs.build
  end

  def create
    @a = A.new(a_params)

    if @a.save
      redirect_to(new_a_path)
    else
      render(:new)
    end
  end

  def a_params
    params.require(:a).permit(:name, bs_attributes: [:name], cs_attributes: [:name])
  end
end

<%= form_for(@a) do |f| %>
  <% if @a.errors.any? %>
    <ul>
      <% @a.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  <% end %>

  <%= f.label(:name) %>
  <%= f.text_field(:name) %>

  <ul>
    BS:
    <%= f.fields_for(:bs) do |ff| %>
      <li>
        <%= ff.label(:name) %>
        <%= ff.text_field(:name) %>
      </li>
    <% end %>
  </ul>

  <ul>
    CS:
    <%= f.fields_for(:cs) do |ff| %>
      <li>
        <%= ff.label(:name) %>
        <%= ff.text_field(:name) %>
      </li>
    <% end %>
  </ul>

  <%= f.submit %>
<% end %>

I've also tried creating it from the ternary table which works for one single association among the models (A-B-C), but it's impossible for multiple (A1-B1-C1, A1-B2-C1). Check out the code:

class AsController < ApplicationController
  def new
    @abc = Abc.new
    @abc.build_a
    @abc.build_b # 2.times { @abc.build_b } only overwrites @abc.b
    @abc.build_c
  end
end

It seems no matter what Rails can only create associations between two models leaving the ternary model missing one association.

I have isolated the issue as a repository's pull request as you can see HERE

0

There are 0 best solutions below