Ruby on rails 3 - Join many models to update a fie

2019-08-18 17:55发布

问题:

I have a problem trying to update an attribute of one model using the value from another model, but those two models are not related.

My models are:

class Order < ActiveRecord::Base
  has_many :order_details
  has_many :waybills
end

class OrderDetail < ActiveRecord::Base
  belongs_to :order, :foreign_key => "order_id"
  belongs_to :product, :foreign_key => "product_id"
end

class Waybill < ActiveRecord::Base
  has_many :purchases
  belongs_to :order, :foreign_key => 'order_id'
end

class Purchase < ActiveRecord::Base
  belongs_to :waybill, :foreign_key => 'waybill_id'
  has_many :detail_purchases
end

class DetailPurchase < ActiveRecord::Base
  belongs_to :purchase, :foreign_key => 'purchase_id'
  belongs_to :product, :foreign_key => 'product_id'
end

So, as you can see... a "DetailPurchase" belongs to an "Order", but in an indirect way. And an "OrderDetail" belongs to an "Order" directly.

I need to update the attribute "quantity" of the product of "OrderDetail" using the attribute "quantity" of the product of "DetailPurchase".

The idea is the "new" OrderDetail.quantity = "old" OrderDeatil.quantity - DetailPurchase.quantity (and obviously the product_id has to be the same)

So, i don't know how to "write" the query using the "rails way" (could be "Model.find()", "Model.where()", "Model.update_attribute()") or just use a raw sql query (by the way, I don't know how to write or execute raw sql queries)

What do you recommend? Thanks

回答1:

If I understand your question, you're trying to find the (loosely) associated OrderDetail for a given DetailPurchase. To do this, you can do:

class DetailPurchase < ActiveRecord::Base
  belongs_to :purchase, :foreign_key => 'purchase_id'
  belongs_to :product, :foreign_key => 'product_id'

  # Goes up the associations to find the Order at the top of the belongs_to associations
  def parent_order
    purchase.waybill.order
  end

  # Given the parent_order, find the first order_detail that matches our product_id
  def order_detail
    parent_order.order_details.first.where(["product_id = ?", product_id])
  end
end

That being said, there are a few things you might want to change:

First and foremost, you almost certainly shouldn't have a model named 'order' -- order is also an ActiveRecord instance method (as in ClassName.all.order("name ASC")), so you're opening yourself up to a world of hurt.

It also seems a bit problematic to be having one of your models updating an aggregate value in another model when it changes; you're going to have all sorts of tricky logic to deal with when editing or deleting OrderDetails. It might make more sense to use an aggregate function for OrderDetail.quantity -- in other words, set OrderDetail.original_quantity on create, then calculate OrderDetail.quantity as OrderDetail.original_quantity minus the sum of the relevant purchase detail quantities. If you want to go this route, I'd ignore the code above and add purchase_details as a scope method to OrderDetail; see the API for details.