简体   繁体   English

在保存块中保存属性的最佳方法?

[英]Best way to save attribute inside save block?

In my controllers I often have functionality like this: 在我的控制器中,我经常具有以下功能:

@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

What's the best way to send an email and update the activation_sent_at attribute without having to save the record twice? 发送电子邮件更新activation_sent_at属性而不必两次保存记录的最佳方法是什么? Calling update_column doesn't feel right to me here because AFAIK it creates an extra SQL query (correct me if I'm wrong). 在这里调用update_column感觉不对,因为AFAIK会创建一个额外的SQL查询(如果我输入错了,请更正我)。

Your code is fine. 您的代码很好。 I wouldn't change it. 我不会改变。

For example, you might be tempted to do something like this: 例如,您可能会想这样做:

@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

Aside from the small issue that there's now repeated logic around @account.guest? 除了小问题之外, @account.guest?现在有重复的逻辑了@account.guest? , what happens if AccountMailer.activation(@account).deliver_later fails? 如果AccountMailer.activation(@account).deliver_later失败怎么办? (When I say "fails", I mean - for example - AccountMailer has been renamed, so the controller returns a 500 error.) (当我说“失败”时,我的意思是-例如AccountMailer已重命名,因此控制器返回500错误。)

In that case, you'd end up with a bunch of account records which have an activation_sent_at but were never sent an email; 在这种情况下,您最终将获得一堆具有activation_sent_at但从未发送过电子邮件的account记录; and you'd have no easy way to distinguish them. 并且您将没有容易的方法来区分它们。

Therefore, this code warrants running two database calls anyway: One to create the record, and then another to confirm that an email was sent. 因此,此代码保证无论如何都要运行两个数据库调用:一个创建记录,然后另一个确认发送了电子邮件。 If you refactor the code to only perform a single database call, then you'll become vulnerable to either: 如果将代码重构为仅执行单个数据库调用,那么您将很容易受到以下任一影响:

  • Sending an email to a non-created user, or 向未创建的用户发送电子邮件,或者
  • Marking a user with activation_sent_at despite o email being sent. 尽管发送了电子邮件,但仍用activation_sent_at标记用户。

The controller should be doing two transactions, not one. 控制器应该执行两次事务,而不是一次。 Which is why I said: Don't change it. 这就是为什么我说:不要更改它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM