Deleting records using nested attributes decreases the counter cache twice

Hi guys,

I have these models:

class Compra < ActiveRecord::Base
  belongs_to :proveedor
  has_many :detalle_de_compras
  has_many :lotes, :through => :detalle_de_compras

  accepts_nested_attributes_for :detalle_de_compras, :allow_destroy =>
true

  def nombre_proveedor
    self.proveedor.try(:nombre)
  end
end

class DetalleDeCompra < ActiveRecord::Base
  belongs_to :compra, :counter_cache => true
  belongs_to :lote

  accepts_nested_attributes_for :lote
end

The problem is that when I delete a "DetalleDeCompra" through an
update to "Compra" using nested attributes (with the _delete param set
to something that evaluates to true) the counter cache is decreased
twice.

I don't know if it's worth mentioning that I'm sending data for a 2
level deep nested structure. This is the deepest model I'm using:

class Lote < ActiveRecord::Base
  belongs_to :estado_del_lote
  belongs_to :producto
  has_one :detalle_de_compra
  has_one :compra, :through => :detalle_de_compra

  def nombre_producto
    self.producto.try(:nombre)
  end

  def nombre_estado_del_lote
    self.estado_del_lote.try(:nombre)
  end

  def fecha_compra
    self.compra.try(:fecha)
  end
end

Any ideas on what's wrong here?

P.S. Here is the migration to the model holding the counter cache:

class CreateCompras < ActiveRecord::Migration
  def self.up
    create_table :compras do |t|
      t.integer :proveedor_id
      t.date :fecha
      t.date :fecha_de_pago
      t.string :condicion_de_pago
      t.integer :detalle_de_compras_count, :default => 0

      t.timestamps
    end
  end

  def self.down
    drop_table :compras
  end
end

Hi, please can you specify which version of Rails are you currently using, and an example of the code that causes your problem?

I tried to reproduce it with:
compra.update_attributes :detalle_de_compras_attributes => [{:id => 953125641, :_delete => true}]

but works fine for me, it decrements the counter once.

Cheers,
Luca

I'm using Rails 2.3.2, on Ubuntu 8.10.

Ok, here is a capture of the "params" sent to create a record, with
the nested params:
# URL: http://localhost:3000/compras/create
# Compra params
compra[fecha] 2009-06-04
compra[proveedor_id] 1
compra[condicion_de_pago] 15 dias
compra[fecha_de_pago] 2009-06-18

# First nested model, DetalleDeCompra params
compra[detalle_de_compras_attributes][0][id]
compra[detalle_de_compras_attributes][0][cantidad] 10
compra[detalle_de_compras_attributes][0][precio_unitario] 123

# Second nested model, Lote params
compra[detalle_de_compras_attributes][0][lote_attributes][cantidad] 10
compra[detalle_de_compras_attributes][0][lote_attributes]
[estado_del_lote_id] 2
compra[detalle_de_compras_attributes][0][lote_attributes][producto_id]
5

Then the params to delete that DetalleDeCompra instance that triggers
the problem:
# URL: http://localhost:3000/compras/update/3
compra[fecha] 2009-06-04
compra[proveedor_id] 1
compra[condicion_de_pago] 15 dias
compra[fecha_de_pago] 2009-06-18

compra[detalle_de_compras_attributes][0][id] 8
compra[detalle_de_compras_attributes][0][cantidad] 10
compra[detalle_de_compras_attributes][0][precio_unitario] 123
compra[detalle_de_compras_attributes][0][_delete] true

compra[detalle_de_compras_attributes][0][lote_attributes][cantidad] 10
compra[detalle_de_compras_attributes][0][lote_attributes]
[estado_del_lote_id] 2
compra[detalle_de_compras_attributes][0][lote_attributes][producto_id]
5

And the code, is the same code that all controllers have in the Update
action:
@data = Compra.find(params[:id])
@data.update_attributes(params[compra])
@data.save

Best regards.

Ah!, forgot to mention that the count then goes from 1 record to -1
records.

Hi, just gave a look at your code, the problem is trivial: you shouldn't call both #update_attributes *and* #save, because they performs the same operation. The former is a syntax shortcut:

person.name = "luca"
person.age = 26
person.save

# or

person.update_attributes :name => "luca", :age => 26

So, updating *twice* your record, the detalle de compras counter will be decreased assuming the value of -1.

Cheers,
Luca

Ah! thank you a lot :slight_smile:
Works flawless now.

Best regards.