So, I have a model Label
which is polymorphic and another model Stuff
which is not. A stuff can have many labels (may also called groups), and each and every one of them can also have a label. I am working with the Cocoon gem to try to add these resource on to a single edit/new form (the Stuff
one). The problem is that when I try to update the stuff with a new group (with new labels in it), it says this Labels labels labelable must exist
. I think that that's an error given because the first label(group) is not yet saved to the database, so it can't give his nested label an id. (not sure though)
Also, is this the best way to do that? I mean, I made the label polymorphic only because I needed to save a only a string, and it would be unpractical and it would have taken database storage for nothing...
Enough talking, here's my code:
<div class="form-group">
<label>Groups:</label>
<div id="group" class="col-md-12 p-0 pl-md-3">
<%= form.fields_for :labels do |groupForm| %>
<%= render 'group_fields', f: groupForm %>
<% end %>
<div class="text-center">
<%= link_to_add_association 'Add Group', form, :labels, partial: 'group_fields', class: 'btn btn-success',
wrap_object: Proc.new { |group| group.labels.build; group } %>
</div>
</div>
</div>
that's in my _form.html.erb
<div class="nested-fields">
<div class="field form-group row">
<%= f.label :name, class: 'col-sm-1 col-form-label' %>
<div class="col-sm-10">
<%= f.text_field :name, class: 'form-control ml-2' %>
</div>
<div class="col-sm-1 p-0">
<%= link_to_remove_association "×".html_safe, f, class: 'badge badge-pill badge-danger mt-2' %>
</div>
<div class="col-12">
<div id="label" class="col-md-12 p-0 pl-md-5 pt-2">
<%= f.fields_for :labels do |labelForm| %>
<%= render 'label_fields', f: labelForm %>
<% end %>
<div class="text-center">
<%= link_to_add_association 'Add Label', f, :labels, class: 'btn btn-success' %>
</div>
</div>
</div>
</div>
</div>
that's in _group_fields.html.erb
<div class="nested-fields">
<div class="field form-group row mb-2">
<%= f.label :name, class: 'col-sm-1 col-form-label' %>
<div class="col-sm-10">
<%= f.text_field :name, class: 'form-control ml-2' %>
</div>
<div class="col-sm-1 p-0">
<%= link_to_remove_association "×".html_safe, f, class: 'badge badge-pill badge-danger mt-2' %>
</div>
</div>
</div>
and that's in my _label_fields.html.erb
class Label < ApplicationRecord
belongs_to :labelable, polymorphic: true
has_many :labels, as: :labelable, dependent: :destroy
accepts_nested_attributes_for :labels, allow_destroy: true, reject_if: proc { |att| att[:name].blank? }
end
this is my Label model
class Stuff< ApplicationRecord
has_many :labels, as: :labelable, dependent: :destroy
accepts_nested_attributes_for :labels, allow_destroy: true, reject_if: proc { |att| att[:name].blank? }
end
and this is my Stuff model
I forgot to mention that if I add only the first "layer" of label (group) without writing anything on the labels (2nd "layer") and I submit the form (which I can do and it updates the database as well) when I come back and edit I can actually modify the 2nd "layer" without any problems.
A belongs_to
relation-ship is by default "required". This means it has to be filled in upon saving. However, when saving an item with nested childs, no id's are yet known and while obvious, rails has no way of knowing the belongs-to
association will be set when saving them together.
There are two ways to handle this:
belongs_to
optional, this will skip the validation, and the save will work. Something like belongs_to :labelable, polymorphic: true, optional: true
inverse_of
so rails knows when saving the labels
it corresponds to the labelable
relationship. Like so:
has_many :labels, as: :labelable, dependent: :destroy, inverse_of: :labelable
You can declare the inverse_of
on any assocation, so also on the belongs_to
, but in most it suffices to just declare it on the has_many
. It might not be enough in your specific scenario.