Подсчитать вложенный элемент перед сохранением - PullRequest
0 голосов
/ 03 ноября 2019

Пусть есть модели Order и Item.

class Order < ApplicationRecord
  has_many :items, inverse_of: :order, dependent: :delete_all

  before_save do
    self.packed_volume = compute_packed_volume
  end

  private

  def compute_packed_volume
    items.count * 0.1O
  end
end

And

class Item < ApplicationRecord
  belongs_to :order, inverse_of: :items
end

Проблема в том, что items.count равен 0, так как элементы еще не созданы. Как мы можем получить количество элементов, которые будут созданы, чтобы использовать его при создании заказа?

Ответы [ 2 ]

1 голос
/ 03 ноября 2019

То, что вы ищете, это «кеш счетчика». Он реализует именно то, что вы пытаетесь сделать. ActiveRecord может сделать это за вас:

belongs_to :post, :counter_cache => true

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

Драгоценный каменьcounter-cache делает работу просто. Другой вариант, counter-culture, делает это немного дальше, включая поддержку подсчета детей в отношениях has_many :through.

Ваш пример немного интереснее, поскольку вы ищете не счет, а вычислениесчет. Таким образом, вы можете либо просто свернуть с этим и использовать его для вычисления pack_volume на лету, возможно, в методе на модели, очень похожем на ваш метод compute_packed_volume().

Если вы хотите сохранить фактический объемв вашей родительской записи (возможно, это очень дорого для вычисления), вам нужно перейти от установки обратных вызовов в родительской модели к размещению их в дочерней модели. Драгоценный камень counter_culture поддерживает это с помощью "Delta Magnitude". Примерно так:

class Item < ActiveRecord::Base
  belongs_to :order
  counter_culture :order, column_name: :packed_volume, delta_magnitude: 0.1
end

class Order < ActiveRecord::Base
  has_many :items
end

Параметр delta_magnitude может взять процесс, так что вы можете делать более сложные вещи. Возможно, что-то вроде этого:

counter_culture :order, column_name: :packed_volume, delta_magnitude: -> {|item| item.length * item.width * item.height }

Вы можете накатить свое собственное решение по этим направлениям, если у вас есть другие требования, исключающие использование драгоценного камня. Вам нужно будет добавить обратные вызовы для Item для after_create и before_destroy, чтобы увеличить / уменьшить родительскую запись. Вам также может понадобиться обновить порядок при изменении записи, если ваши вычисления объема становятся более сложными.

0 голосов
/ 03 ноября 2019

Вместо этого попробуйте size, он не будет выполнять запрос

items.size * 0.1O

Надеюсь, это поможет!

...