Rails ActiveRecord: pluck и: map возвращают два разных значения - почему? - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть модель ActiveRecord (с использованием STI) MonetaryChange::PaymentPromise, которая имеет subtotal_cents в качестве атрибута / столбца (целое число).

Мой контроллер обновляет эту модель / атрибут, но по какой-то причине я получаю старое / оригинальное значение, когда я вызываю map, и обновленное (правильное) значение, когда я вызываю pluck. Например:

V410MonetaryChange::PaymentPromise.where(id: 1).pluck(:subtotal_cents)
=> [24600] # this is the correct updated value

V410MonetaryChange::PaymentPromise.where(id: 1).map(&:subtotal_cents)
=> [12300] # this is the original value, but it should have been updated to 24600

Я заметил, что SQL, который Rails генерирует для метода pluck, намного проще:

(0.5ms)  SELECT "v410_monetary_changes"."subtotal_cents" 
FROM "v410_monetary_changes" 
WHERE "v410_monetary_changes"."type" IN ('V410MonetaryChange::PaymentPromise') 
AND "v410_monetary_changes"."deleted_at" IS NULL 
AND "v410_monetary_changes"."id" = $1  [["id", 1]]

но SQL для метода map загружает целую кучу ассоциаций после первого запроса:

  V410MonetaryChange::PaymentPromise Load (0.6ms)  SELECT "v410_monetary_changes".* FROM "v410_monetary_changes" WHERE "v410_monetary_changes"."type" IN ('V410MonetaryChange::PaymentPromise') AND "v410_monetary_changes"."deleted_at" IS NULL AND "v410_monetary_changes"."id" = $1  [["id", 1]]
  V410OrderedSku Load (0.3ms)  SELECT  "v410_ordered_skus".* FROM "v410_ordered_skus" WHERE "v410_ordered_skus"."deleted_at" IS NULL AND "v410_ordered_skus"."id" = $1 LIMIT 1  [["id", 1]]
  V410OrderedSkusV410Sku Load (0.2ms)  SELECT  "v410_ordered_skus_v410_skus".* FROM "v410_ordered_skus_v410_skus" WHERE "v410_ordered_skus_v410_skus"."v410_ordered_sku_id" = $1 LIMIT 1  [["v410_ordered_sku_id", 1]]
  V410Sku Load (0.2ms)  SELECT  "v410_skus".* FROM "v410_skus" WHERE "v410_skus"."id" = $1 LIMIT 1  [["id", 1]]

Есть идеи, почему это так, и как я могу решить эту проблему?

Я пытался добавить .reload к моему запросу, но результаты были такими же:

V410MonetaryChange::PaymentPromise.where(id: 1).reload.map(&:subtotal_cents)
=> [12300]

Редактировать 1: Я могу подтвердить, что база данных имеет правильное значение (24600). Когда я открываю консоль psql и запускаю запрос:

SELECT "v410_monetary_changes"."subtotal_cents" 
    FROM "v410_monetary_changes" 
    WHERE "v410_monetary_changes"."type" IN ('V410MonetaryChange::PaymentPromise') 
    AND "v410_monetary_changes"."deleted_at" IS NULL 
    AND "v410_monetary_changes"."id" = 1;

Я получаю:

 subtotal_cents 
----------------
          24600
(1 row)

Так что Rails должен что-то делать с ассоциациями, из-за которых он дает мне старую версию записи. Есть идеи почему?

1 Ответ

0 голосов
/ 12 ноября 2018

ОК, важный урок Rails, полученный сегодня!

Недавно я добавил к этой модели обратный вызов after_initialize, который устанавливает значения по умолчанию (включая subtotal_cents) для вновь инициализированных записей.

Что я не понял, так это то, что этот обратный вызов был re -инициализирующим (то есть перезаписывал в памяти после загрузки) существующих записей со значениями по умолчанию.

Мое исправление должно было измениться

after_initialize :set_default_currency_amounts

до

after_initialize :set_default_currency_amounts, unless: Proc.new { |pp| pp.persisted? }

В моем случае именно поэтому SQL был таким длинным - мои значения по умолчанию требовали загрузки некоторых связанных моделей, чтобы получить значение по умолчанию для subtotal_cents. Это была подсказка, которая помогла мне решить эту проблему.

Спасибо всем за помощь!

...