[英]Ruby on Rails: Value of database field depends on how accessed?
我有一個表現出奇怪行為的電子商務應用程序。 該產品是培訓課程。 學生為各個班級創建預訂,這些預訂將作為訂單項添加到購物車中。 在某個時候,訂單被創建並支付,這時我需要遍歷每個預訂,並更新“已支付”屬性。 所以這是數據庫的構造...
1) Class has_many Reservations
2) Reservation belongs_to class, and has_one line_item (and has a decimal :paid attribute)
3) Line_item belongs_to reservation, and belongs_to cart
4) Cart has_many line_items, and has_one order
因此,當學生創建訂單時,將執行信用卡交易,如果成功,我想生成收據並將其通過電子郵件發送給學生。
最初,在我的OrdersController#create方法中看起來像這樣...
def create
@cart = current_cart #retrieves the cart
@order = @cart.build_order(params[:order])
# snip saving order and purchase transaction
# I would then iterate over the line-items with
@cart.line_items.each { |li| li.reservation.update_attribute(:paid, li.reservation.fee)}
但是,此時我發現,付費屬性的值將取決於line_item的訪問方式。 如果我通過...訪問
@cart.line_items.each {|li| li.reservation.paid }
值將被設置。 但是,如果使用過...
@order.cart.line_items.each {|li| li.reservation.paid }
該值將為零。 (但是它正在訪問適當的reservation.id)
然后我發現,如果我更改將付費屬性設置為以下內容的行...
@order.cart.line_items.each { |li| li.reservation.update_attribute(:paid, li.reservation.fee)}
以上任一訪問將返回正確的值。
所以我想我的問題是,設置:paid屬性與有什么區別
@cart.line_items.each ...
和
@order.cart.line_times.each ...
基於下面的評論,我將迭代移入了Cart模型。 現在有
def paid_deposit!
self.line_items.includes(:reservation).each do |li|
li.reservation.update_attribute(:paid, li.reservation.deposit)
end
end
但是,存在相同的問題控制器具有以下代碼...
@cart.paid_deposit!
@cart.line_items.each {|li| logger.debug "Cart: Res: #{li.reservation.id}, Paid: #{li.reservation.paid}" }
@order.cart.line_items.each {|li| logger.debug "Order: Res: #{li.reservation.id}, Paid: #{li.reservation.paid}" }
結果日志是...
Reservation Load (0.3ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` IN (346, 347, 348)
(0.2ms) BEGIN
(0.2ms) UPDATE `reservations` SET `paid` = 40.0, `updated_at` = '2012-02-05 22:12:36' WHERE `reservations`.`id` = 346
(0.4ms) COMMIT
(0.1ms) BEGIN
(0.1ms) UPDATE `reservations` SET `paid` = 40.0, `updated_at` = '2012-02-05 22:12:36' WHERE `reservations`.`id` = 347
(0.6ms) COMMIT
(0.1ms) BEGIN
(0.1ms) UPDATE `reservations` SET `paid` = 25.0, `updated_at` = '2012-02-05 22:12:36' WHERE `reservations`.`id` = 348
(0.3ms) COMMIT
LineItem Load (0.1ms) SELECT `line_items`.* FROM `line_items` WHERE `line_items`.`cart_id` = 24
Reservation Load (0.2ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` = 346 LIMIT 1
Cart: Res: 346, Paid: 40.0
Reservation Load (0.1ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` = 347 LIMIT 1
Cart: Res: 347, Paid: 40.0
Reservation Load (0.1ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` = 348 LIMIT 1
Cart: Res: 348, Paid: 25.0
Order: Res: 346, Paid: 0.0
Order: Res: 347, Paid: 0.0
Order: Res: 348, Paid: 0.0
可以看出,從購物車中引用付款金額會獲得正確的值,而從Order中執行相同的操作就不會。 顯然使用緩存的值? 它不訪問數據庫。
這是一種更新這樣的事情的可怕方法。 我希望您有一個很好的單元測試或功能測試來證明這種損壞的行為,以便可以確定正確時將其修復。
您可能應該制作一個模型方法來幫助簡化此類事情。 由於無法從交互式控制台輕松訪問控制器環境,因此在控制器中進行了大量繁重的工作使測試變得困難。
我不知道的是,如何調用諸如paid
的訪問器方法在each
循環中有什么用處。 那應該返回一個數字,而不是設置任何東西。 update_attribute
版本應該可以工作,因為它實際上正在更改數據。
您可以做的是將其推入模型,並創建一個執行以下操作的方法:
class Cart < ActiveRecord::Base
def paid!
self.line_items.include(:reservation).each do |li|
li.update_attribute(:paid, li.reservation.fee)
end
end
end
如果您可以在數據庫中完成所有操作,則速度可能會更快:
class Cart < ActiveRecord::Base
def paid!
self.line_items.include(:reservation).update_all(
'paid=reservations.fee'
)
end
end
通常,我只是尋找一些方法來避開模型,如果它們只是扭曲那些不受驗證檢查的字段。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.