【问题标题】:Best way to save attribute inside save block?在保存块内保存属性的最佳方法?
【发布时间】:2018-07-01 10:32:34
【问题描述】:

在我的控制器中,我经常有这样的功能:

@account = Account.new(account_params)
if @account.save
  if @account.guest?
    ...
  else
    AccountMailer.activation(@account).deliver_later
    @account.update_column(:activation_sent_at, Time.zone.now)
    flash[:success] = "We've sent you an email."
    redirect_to root_path
  end
end

发送电子邮件更新activation_sent_at属性而不必保存记录两次的最佳方式是什么?在这里调用 update_column 对我来说感觉不对,因为 AFAIK 它会创建一个额外的 SQL 查询(如果我错了,请纠正我)。

【问题讨论】:

  • 解决方法是使用assign_attributes,但我不推荐它。您的代码很好,并且更新列的附加更新查询应该没有太大区别。此外,guest? 之类的检查也适用于某些不会使用 assign_attributes 调用的模型挂钩。使用它的总成本将使它有点难以阅读
  • 为了优化,可以通过配置使用deliver_later通过ActiveJob发送邮件;这将使它异步。这将确保您的控制器不会花时间撰写、处理邮件提供商和发送电子邮件。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

你的代码没问题。我不会改变它。

例如,您可能很想做这样的事情:

@account = Account.new(account_params)
@account.activation_sent_at = Time.zone.now unless @account.guest?
if @account.save
  if @account.guest?
    ...
  else
    AccountMailer.activation(@account).deliver_later
    flash[:success] = "We've sent you an email."
    redirect_to root_path
  end
end

除了现在围绕 @account.guest? 重复逻辑的小问题之外,如果 AccountMailer.activation(@account).deliver_later 失败会发生什么?(当我说“失败”时,我的意思是 - 例如 - AccountMailer已重命名,因此控制器返回 500 错误。)

在这种情况下,您最终会得到一堆 account 记录,其中包含 activation_sent_at 但从未发送过电子邮件;而且你没有简单的方法来区分它们。

因此,无论如何,这段代码保证运行两个数据库调用:一个用于创建记录,另一个用于确认电子邮件已发送。如果您将代码重构为仅执行一次数据库调用,那么您将容易受到以下任何一种攻击:

  • 向未创建的用户发送电子邮件,或
  • 尽管发送了电子邮件,但仍使用 activation_sent_at 标记用户。

控制器应该做两个事务,而不是一个。这就是为什么我说:不要改变它。

【讨论】:

  • after_*\before_* 回调是处理 @account.activation_sent_at = Time.zone.now 之类的更好的地方
  • @Зелёный 真的吗?仅仅因为它是 rails 框架的一部分,这并不能成为一个“更好的地方”。写after_save :set_activation_sent_at unless :guest? 可能会将逻辑移出控制器,但它仍然会导致我上面描述的完全相同的问题——只是现在,它更加复杂。
  • 保持简洁,瘦控制器,胖模型。
  • 在我看来,您的建议并没有简化事情。此外,理想情况下,模特也应该很瘦。如果你觉得有更好的方式来写这篇文章,我很想看到它的全部内容。
  • 我会考虑在User 中添加一个send_activation_mail 方法——调用AccountMailer 并更新activation_sent_at 列。
猜你喜欢
  • 2012-12-24
  • 1970-01-01
  • 2013-01-08
  • 2021-12-17
  • 2011-04-04
  • 2011-12-13
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多