【问题标题】:Rails Model Callback Not Saving Instance Method ResultsRails 模型回调不保存实例方法结果
【发布时间】:2020-01-09 00:50:53
【问题描述】:

我正在努力让我的模型回调函数正常运行:( 使用 Rails 5.2.4.1、Ruby 2.6.3 和 pg ~> 0.21

我有一个模型“Batch”,一旦它的“Price”和“Quantity”值大于零,我希望它自动计算和更新它自己的“Value”属性。

  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

在我的种子文件中,我创建了一些具有指定数量和价格的 Batch 实例,并将值保留为默认值 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

我不确定我是否遗漏了一些非常明显的东西,但价值似乎永远不会更新

我已经尝试将 save 添加到该方法中,但它似乎也不起作用。我发现保存在这些回调中的其他一些行为非常奇怪。

例如,我使用此实例方法通过连接表将货币分配给批次:

  after_find :assign_currency

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

如果我取消注释“保存”(或将其设为“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>,

我还是 Rails 的新手,所以非常感谢任何建议或建议!这感觉应该很简单,这让我发疯了......

【问题讨论】:

  • 我认为你应该使用 before_save 而不是 after_find

标签: ruby-on-rails ruby postgresql activerecord callback


【解决方案1】:

我建议使用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,我认为this StackOverflow answer 可能会帮助您更了解它。

您可以在将after_find 定义为如下方法后使用它:

def after_find
  <your code to set value>
end

after_initialize & after_find callbacks order in Active Record object life cycle?

【讨论】:

    【解决方案2】:

    在你的模型中怎么样?

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

    【讨论】:

      猜你喜欢
      • 2022-01-24
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 2012-12-26
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 2014-04-15
      相关资源
      最近更新 更多