Question

I have an Order/LineItem type application which inserts an Order and its associated Line Items into a database. After the order is created (after_create) I then call a function which updates a totals field in the Order table. However, when this runs it also runs an update query on all the line items, and because a paper_trail is active for :update, it causes new versions to be inserted too. The problem is, there can be hundreds of line items (the largest so far was 872), so obviously this isn't very optimised and because of the paper trail, we get unnecessary versions.

I've looked at the :touch symbol but this only seems to work child > parent and not parent > child.

Is there a way to stop the updates from being fired on the line items when their parent is updated?

Here are the condensed controller/models.

OrdersController:

class OrdersController < ApplicationController

def create
    @order = Order.new(order_params)

    if @order.save
       render :json => @order, :serializer => ::OrderSerializer
    end
end

Order Model:

class Order < ActiveRecord::Base
    has_paper_trail
    belongs_to :customer
    has_many :line_items, :dependent => :destroy

    after_create :update_total_quantities

    accepts_nested_attributes_for :line_items

    def update_total_quantities
       update_attributes({:total_quantity => calculated_total_quantity})
    end
end

LineItem model:

class LineItem < ActiveRecord::Base
   has_paper_trail :on => [:update, :destroy]   

   belongs_to :order 
end

Example of the problem from the logs:

INSERT INTO "line_items" ("colour", "created_at", "order_id", "price", "product_id", "quantity", "size", "updated_at", "variant_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"[0m  [["colour", 2], ["created_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00], ["order_id", 1216], ["price", 77.0], ["product_id", 241702], ["quantity", 1], ["size", "18"], ["updated_at", Wed, 08 Jan 2014 11:47:38 UTC+00:00], ["variant_id", "241702_2_18"]
INSERT INTO "line_items" ("colour", "created_at", "order_id", "price", "product_id", "quantity", "size", "updated_at", "variant_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"  [["colour", 106], ["created_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00], ["order_id", 1216], ["price", 20.0], ["product_id", 2753], ["quantity", 1], ["size", "18"], ["updated_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00], ["variant_id", "2753_106_18"]

UPDATE "orders" SET "total_quantity" = $1, "updated_at" = $2 WHERE "orders"."id" = 1216  [["total_quantity", 878], ["updated_at", Wed, 08 Jan 2014 11:47:40 UTC +00:00]
UPDATE "line_items" SET "created_at" = $1, "updated_at" = $2 WHERE "line_items"."id" = 108533  [["created_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00], ["updated_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00]
UPDATE "line_items" SET "created_at" = $1, "updated_at" = $2 WHERE "line_items"."id" = 108526[0m  [["created_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00], ["updated_at", Wed, 08 Jan 2014 11:47:38 UTC +00:00
Was it helpful?

Solution

I fixed this by changing the workflow logic. Instead of running the update_total_quantities method after creating it, I run the method in the controller instead, so my updated code looks more like:

OrdersController:

class OrdersController < ApplicationController

def create
  @order = Order.new(order_params)
  @order.total_quantity = @order.calculated_total_quantity

  if @order.save
    render :json => @order, :serializer => ::OrderSerializer
  end
end

Order Model:

class Order < ActiveRecord::Base
  has_paper_trail

  belongs_to :customer
  has_many :line_items, :dependent => :destroy

  accepts_nested_attributes_for :line_items

end   
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top