Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-2331

Saving duplicates on 2 levels embedded nested attributes

    • Type: Icon: Task Task
    • Resolution: Done
    • 3.0.7
    • Affects Version/s: None
    • Component/s: None

      Hi,

      I've got a strange behavior on 2 levels nested attributes with mongoid-3.0.5 (forms are handled by simple_form and cocoon).

      I have 3 models : Node, Volume and Filesystem.

      Node has_many :volumes and Volume embeds_many :filesystems.

      When adding new filesystems to an existing volume, those filesystems are persisted twice, with the same ID.

      When destroying one of those filesystems, both are destroyed (since they have the same id).

      The problem does not occurs when :

      _ Adding filesystems on a _new* volume (both created on the same time)

      • Using Volume has_many :filesystems instead of Volume embeds_many :filesystems (+ belongs_to instead of embedded_in in Filesystem)

      Code samples :

      (I just skip validations, which are just presence, inclusion and numericality, and custom instance methods)

      volume.rb header :
      class Volume
        
        # INCLUSIONS
      
          include Mongoid::Document
          include Mongoid::Timestamps
      
        # RELATIONS
        
          belongs_to :node
          embeds_many :filesystems
          accepts_nested_attributes_for :filesystems, allow_destroy: true, reject_if: proc { |attributes| attributes.all? {|k,v| v.blank? || ['size_unit'].include?(k)} }
      
        # FIELDS
        
          field :name
          field :size
          field :size_unit, default: :gb
      
      [...]
      
      filesystem.rb header :
      class Filesystem
        
        # INCLUSIONS
      
          include Mongoid::Document
          include Mongoid::Timestamps
      
        # RELATIONS
        
          embedded_in :volume
      
        # FIELDS
        
          field :mount_point
          field :name
          field :size
          field :size_unit, default: :gb
      
      [...]
      
      nodes_controller.rb update method :
          def update
            @node= platform.nodes.find_by name: params[:id]
      
            if @node.update_attributes params[:node]
              flash[:success] = t_flash :update_success, @node        redirect_to platforms_platform_node_path(organization, platform, @node)
            else
              flash.now[:error] = t_flash :update_error, @node, count: @node.errors.count
              render :form
            end
          end
      

      params hash :

      (I only paste params[:node][:volume_attributes] part since Node has many other nested attributes)

      When creating a new Volume with a Filesystem :
      volumes_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
          '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
            name: rootvg
            size: '100'
            size_unit: gb
            filesystems_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
              '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
                mount_point: /
                name: rootlv
                size: '10'
                size_unit: gb
      

      Gives :

      irb(main):004:0> Node.find_by(name: 'Server1').volumes.first.filesystems.each{|f| puts f.inspect}
      #<Filesystem _id: 503f78226fad9df0220000f2, _type: nil, created_at: nil, updated_at: nil, mount_point: "/", name: "rootlv", size: "10", size_unit: "gb">
      
      When adding a new Filesystem to the same Volume :
        volumes_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
          '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
            name: rootvg
            size: '100'
            size_unit: gb
            filesystems_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
              '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
                mount_point: /
                name: rootlv
                size: '10'
                size_unit: gb
              '1346337168506': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
                mount_point: /home
                name: homelv
                size: '20'
                size_unit: gb
      

      (Volume and first Filesystem IDs do not appear here since it's the filtered params hash but they are sent in the post request)

      Gives :

      irb(main):008:0> Node.find_by(name: 'Server1').volumes.first.filesystems.each{|f| puts f.inspect}
      #<Filesystem _id: 503f78226fad9df0220000f2, _type: nil, created_at: nil, updated_at: nil, mount_point: "/", name: "rootlv", size: "10", size_unit: "gb">
      #<Filesystem _id: 503f79a16fad9df0220000fa, _type: nil, created_at: nil, updated_at: nil, mount_point: "/home", name: "homelv", size: "20", size_unit: "gb">
      #<Filesystem _id: 503f79a16fad9df0220000fa, _type: nil, created_at: nil, updated_at: nil, mount_point: "/home", name: "homelv", size: "20", size_unit: "gb">
      

      Note : I've just noted when pasting this log that filesystems timestamps fields are created with nil value...which is not the case of the Volume :

      `irb(main):024:0> Node.find_by(name: 'Server1').volumes.first
      => #<Volume _id: 503f78226fad9df0220000ef, _type: nil, created_at: 2012-08-30 14:26:42 UTC, updated_at: 2012-08-30 14:37:03 UTC, node_id: "503f78066fad9df0220000e9", name: "rootvg", size: "100", size_unit: "gb">
      

            Assignee:
            durran Durran Jordan
            Reporter:
            gauthier-delacroix gauthier-delacroix
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: