【问题标题】:Rails ActiveRecord :pluck and :map return two different values - why?Rails ActiveRecord :pluck 和 :map 返回两个不同的值 - 为什么?
【发布时间】:2019-04-15 10:21:41
【问题描述】:

我有一个 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

我注意到 Rails 为 pluck 方法生成的 SQL 要简单得多:

(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]]

map 方法的 SQL 在第一次查询后会加载一大堆关联:

  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 一定是对关联进行了处理,导致它向我提供了旧版本的记录。任何想法为什么?

【问题讨论】:

  • 应该是一样的。尝试重新启动您的 Rails 控制台。
  • @EvanG - 是的,我也想过。可悲的是,重新启动控制台没有任何改变!
  • 您是否覆盖了模型中的 subtotal_cents
  • @MarcinKołodziej 我在父模型中有另外两个 subtotal_cents 方法,但在我查询的模型中没有。

标签: ruby-on-rails ruby-on-rails-4 activerecord


【解决方案1】:

好的,今天学到了重要的 Rails 课程!

我最近为这个模型添加了一个after_initialize 回调,它为新初始化的记录设置默认值(包括subtotal_cents)。

我没有意识到这个回调是重新-初始化(即加载后在内存中覆盖)现有具有默认值的记录。

我的解决办法是改变

after_initialize :set_default_currency_amounts

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

在我的例子中,这就是 SQL 如此长的原因 - 我的默认设置需要加载一些关联模型才能获得 subtotal_cents 的默认值。这是帮助我解决这个问题的线索。

感谢大家的帮助!

【讨论】:

  • 我想你开始意识到所有的 ActiveRecord 回调都是邪恶的。我的目标是将after_initialize 移至工厂,这将有助于创建您的记录。在我看来,通过调用 Model.new 运行 3 个查询是一个令人讨厌的副作用。
  • 你应该可以只说unless: :persisted?或者if: :new_record?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-22
  • 2014-02-10
相关资源
最近更新 更多