Ruby on Rails: значение поля базы данных зависит от того, как осуществляется доступ? - PullRequest
0 голосов
/ 05 февраля 2012

У меня есть приложение электронной коммерции, которое демонстрирует странное поведение. Продукт представляет собой учебный класс. Студент создает резервирование для различных классов, которые добавляются в корзину в качестве отдельных позиций. В какой-то момент и заказ создан и оплачен, после чего мне нужно выполнить итерацию по каждому из резервирований и обновить атрибут «заплатил». Итак, вот строение базы данных ...

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 # метод создания выглядел примерно так ...

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)}

Однако в этот момент я обнаружил, что значение атрибута paid будет зависеть от того, как был получен доступ к line_item. Если бы я получил доступ через ...

@cart.line_items.each {|li| li.reservation.paid }

значения будут такими, как установлено. ОДНАКО, если бы он использовал ...

@order.cart.line_items.each {|li| li.reservation.paid }

Значение будет равно нулю. (Но это был доступ к правильному резервированию. Идентификатор)

Затем я обнаружил, что если я изменил строку, в которой установлен атрибут оплачиваемого, на следующий ...

  @order.cart.line_items.each { |li| li.reservation.update_attribute(:paid, li.reservation.fee)}

что любой из вышеперечисленных обращений вернул бы правильное значение.

Так что я думаю, мой вопрос, в чем разница при установке атрибута: paid с

@cart.line_items.each ...

и

@order.cart.line_times.each ...

На основании приведенного ниже комментария я переместил итерацию в модель корзины. теперь оно имеет

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

Как видно, ссылка на оплаченную сумму из корзины получает правильные значения, а то же самое из Заказа - нет. Видимо, используя кэшированные значения? Нет доступа к базе данных.

1 Ответ

0 голосов
/ 06 февраля 2012

Это довольно ужасный способ обновления таких вещей. Я надеюсь, что у вас есть хороший тестовый модуль или функциональный тест, чтобы продемонстрировать это неработающее поведение, поэтому вы можете быть уверены, что оно исправлено, когда вы правильно поняли.

Вам, вероятно, следует создать модельный метод, чтобы облегчить подобные вещи. Выполнение большого количества операций в контроллере затрудняет тестирование, поскольку вы не можете легко получить доступ к среде контроллера из интерактивной консоли.

Что я не могу понять, так это то, что вызов метода доступа типа 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

Обычно я ищу способы обойти модель, если просто перевернуть поля, которые не подлежат проверкам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...