0

I am trying to create multiple checkbox tags for second level embedded documents. However, now it just creates new objects. I have models

class Paprasa
  include Mongoid::Document

  attr_accessible :id, :bkompetencijas, :bkompetencijas_attributes

  accepts_nested_attributes_for :etapa
  accepts_nested_attributes_for :bkompetencijas, :allow_destroy => true
  embeds_many :bkompetencijas , class_name: 'Kompetencija', inverse_of: :paprasabk,  :cascade_callbacks => true
end
#-----------------------------------------------
class Kompetencija
  include Mongoid::Document

  attr_accessible :id, :paprasabk, :paprasabk_attributes, :siekinys, :siekinys_attributes  

  accepts_nested_attributes_for :paprasa, :allow_destroy => true
  accepts_nested_attributes_for :siekinys, :allow_destroy => true

  embedded_in :paprasabk, class_name: 'Paprasa', inverse_of: :bkompetencijas
  embeds_many :siekinys, class_name: 'Siekiny'  , inverse_of: :kompetencija, :cascade_callbacks => true
end
#-----------------------------------------------
class Siekiny
  include Mongoid::Document
  field :matricos_ids, :type => Array, :default => []

  attr_accessible :id, :kompetencija, :kompetencija_attributes, :matricos_ids

  accepts_nested_attributes_for :kompetencija

  embedded_in :kompetencija  , class_name: 'Kompetencija', inverse_of: :siekinys
end

a form

<%= form_for  [@paprasa],:html => { :multipart => true} do |f| %>
    <%= f.fields_for :dkompetencijas do |form_inner| %>
         <%= form_inner.fields_for :siekinys do |form_inner_inner| %>
          <% @some_other_object.each_with_index do |d,indexx| %>
              <%= check_box_tag "#{field_name_for_js(form_inner_inner, "matricos_ids")}[]", d.id, form_inner_inner.object.matricos_ids.include?(d.id), :id => "#{field_id_for_js(form_inner_inner, "matricos_id_") << d.id.to_s}" ).to_s %>  
          <% end %>
         <% end %> 
    <% end %>
<% end %>

where

def field_id_for_js(builder, attribute)
      "#{builder.object_name}[#{attribute.to_s.sub(/\?$/,"")}]".gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
  end

  def field_name_for_js(builder, attribute)
      "#{builder.object_name}[#{attribute.to_s.sub(/\?$/,"")}]"
  end

it gives me ouput like this

<input id="paprasa_dkompetencijas_attributes_0_siekinys_attributes_0_matricos_id_506567916226f718e50000cb" name="paprasa[dkompetencijas_attributes][0][siekinys_attributes][0][matricos_ids][]" type="checkbox" value="506567916226f718e50000cb" />
<input id="paprasa_dkompetencijas_attributes_0_siekinys_attributes_1_matricos_id_506567916226f718e50000cb" name="paprasa[dkompetencijas_attributes][0][siekinys_attributes][1][matricos_ids][]" type="checkbox" value="506567916226f718e50000cb" />

and parameters

{"paprasa"=>{"dkompetencijas_attributes"=>{"0"=>{"siekinys_attributes"=>{"0"=>{"id"=>"505adfd26226f7555a000191", "matricos_ids"=>["506567916226f718e50000cb"]}, "1"=>{"id"=>"507558176226f75fa5000033"}, "2"=>{"id"=>"512f19626226f765c7000071"}, "3"=>{"id"=>"512f36456226f765c70000c8"}}, "id"=>"505adfd26226f7555a000190"}}}, "user_id"=>"5058514b6226f73ae4000064", "etapa_id"=>"505851516226f73ae4000065"}

this just creates new "siekiny" objects instead of updating existing ones. What I am doing wrong?

Aitvaras
  • 261
  • 4
  • 15
  • Sometimes pasting the code is just not enough especially when you are using a non-English language in code. Can you explain what exactly you want to achieve? I mean do you want to create new `dkompetencijas` while creating `paprasa` and want to assign existing `siekinys` to `dkompetencijas`? – Manoj Monga Mar 01 '13 at 15:57
  • paprasa is the root object. it has many dkompetencijas (child objects). Every dkompetencijas have also many siekinys. I want to update array field on every siekinys at once. However, I must do it through the root object. Creating, updating and other stuff works for paprasa dkompetencijas and siekinys. – Aitvaras Mar 03 '13 at 10:27

2 Answers2

1

I actually ran into this problem recently. Accepts_nested_attributes_for cannot(yet) decipher with a new parent object form whether to create new child objects or find and update existing ones. If you don't want your child objects being duplicated when you create a new parent object then you will have to create a custom setter method to handle the find_or_create for you.

Checkout THIS question which solves the problem by overriding autosave associations.

Or, checkout THIS question which solves the problem by using reject_if and a custom method.

Another option to solve this problem is to break down the hash in the controller and do all object creating there instead of in the model.

Community
  • 1
  • 1
rocket scientist
  • 2,307
  • 1
  • 16
  • 27
  • OK. The first your reference was really helpfull. I needed hidden field after `check_box_tag` `` [another source](http://www.ruby-forum.com/topic/103195) [your posted source](http://stackoverflow.com/questions/3579924/accepts-nested-attributes-for-with-find-or-create) – Aitvaras Mar 04 '13 at 10:45
  • Also somehow `form_inner_inner.object.matricos_ids.include?(d.id)` and `'0'` from hidden_field should be changed to `(form_inner_inner.object.matricos_ids.include?(d.id) ? true : false)` – Aitvaras Mar 04 '13 at 13:27
1

However, this did not work for me. For some actions it worked several times and then broke again. I even tried from @some_other_object side (this object is referenced from paprasa and it just kept creating new objects. The only way I got this was putting some "hackish" things in controller, which I really don't like. I took parent object, iterated through child using index on params to look for specified array field and assigned to child attribute, then the .save method. Now everything works, but i do not Like it...

Aitvaras
  • 261
  • 4
  • 15
  • Another option you can do if you don't like the hacky code in your models is move the logic to your controller and handle the submitted hash there. To handle this problem I ended up breaking up the forms bc after hacking together setter methods it didnt sit well with me either. Hopefully rails will solve the problem for us soon :) – rocket scientist Mar 05 '13 at 13:23
  • thats what i did - handled hash in controller. – Aitvaras Mar 05 '13 at 16:16