Обратный вызов модели Rails не сохраняет результаты метода экземпляра - PullRequest
1 голос
/ 09 января 2020

Я изо всех сил пытаюсь заставить мои функции обратного вызова модели работать должным образом :( Используя Rails 5.2.4.1, Ruby 2.6.3 и pg ~> 0.21

У меня есть модель "Batch", которую я хочу чтобы автоматически вычислять и обновлять свой собственный атрибут «Значение», если его значения «Цена» и «Количество» больше нуля.

  def change
    create_table :batches do |t|
      t.references :product, foreign_key: true, null: false
      t.references :currency, foreign_key: true
      t.string :batch_number, null: false
      t.string :status, null: false, default: "pending"
      t.integer :quantity, null: false, default: 0
      t.integer :price, null: false, default: 0
      t.integer :value, null: false, default: 0

      t.timestamps
    end
  end
end

В моем начальном файле я создаю несколько экземпляров пакета с указанным количеством, а затем Цена и оставьте значение по умолчанию 0 (это будет добавлено позже при создании экземпляра Order):

batch1 = Batch.new(
  product_id: Product.last.id,
  batch_number: "0001-0001-0001",
  quantity: 1800
)

if batch1.valid?
  batch1.save
  p batch1
else
  p first_batch1.errors.messages
end

batch1.price = 3
batch1.save

Тогда начинаются мои проблемы ... Я попробовал несколько подходов, похожих на ниже:

after_find :calculate_value

def calculate_value
  self.value = price * quantity if value != price * quantity
end

Я не уверен, что здесь упускаю что-то очень очевидное, но значение никогда не обновляется .

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

Например, я назначаю валюту для пакета через присоединиться к таблице с помощью этого метода экземпляра :

  after_find :assign_currency

  def assign_currency
    self.currency_id = currency.id unless currency.nil?
    # save
  end

Если я раскомментирую это "save" (или сделаю его "self.save"), тогда начальный файл создает пакеты, но затем не может создать таблицу соединения, возвращая {: batch => [ "должен существовать"]}. Тем не менее, в консоли, пакет делает:

[#<Batch:0x00007fb874ad0aa0
  id: 1,
  product_id: 1,
  batch_number: "0001-0001-0001",
  status: "pending",
  quantity: 1800,
  currency_id: nil,
  price: 0,
  value: 0,
  created_at: Thu, 09 Jan 2020 00:38:42 UTC +00:00,
  updated_at: Thu, 09 Jan 2020 00:38:42 UTC +00:00>,

Я все еще новичок в рельсах, поэтому был бы очень признателен за любые советы или предложения вообще! Такое ощущение, что все должно быть просто, и это сводит меня с ума ...

Ответы [ 2 ]

0 голосов
/ 09 января 2020

Я бы рекомендовал использовать before_validation обратный вызов вместо after_find. Причина, по которой я бы порекомендовал это, заключается в том, что в after_find столбец value будет заполняться только тогда, когда объект загружается с использованием поиска (.find, .find_or_create), и, следовательно, вы не сможете проверить столбец value перед сохранением. Фактически, при первоначальном сохранении столбец value будет пустым.

Порядок выполнения обратных вызовов следующий:

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback

Так что в вашем случае это может сработать :

before_validation :calculate_value, if: :price_or_quantity_changed?

validates :value, presence: true  # This can be added because the before_validation callback will ensure that value is present

def calculate_value
  self.value = price * quantity if value != price * quantity
end

private

def price_or_quantity_changed?
  self.price_changed? || self.quantity_changed?
end

_changed? методы из модуля ActiveModel :: Dirty , который помогает нам отслеживать значения, которые изменились в записи.

Однако, если вы захотите использовать after_find, я думаю этот ответ StackOverflow может помочь вам понять его подробнее.

Вы можете использовать after_find после определения это как метод следующим образом:

def after_find
  <your code to set value>
end

порядок обратных вызовов after_initialize & after_find в жизненном цикле объекта Active Record?

0 голосов
/ 09 января 2020

как насчет этой модели?

after_find :calculate_value

def calculate_value
  self.value = self.price * self.quantity if self.value != self.price * self.quantity
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...