【问题标题】:Rails how to update a column after saving?Rails如何在保存后更新列?
【发布时间】:2011-08-12 05:46:25
【问题描述】:

我想为我的评分操作创建一个 after_save 方法。它将划分 rating_score/ratings 并更新列评级。

class KonkurrancersController < ApplicationController
  def rate
    @konkurrancer = Konkurrancer.find(params[:id])
    @container = "Konkurrancer"+@konkurrancer.id.to_s

    @konkurrancer.rating_score += params[:vind][:rating].to_i
    @konkurrancer.ratings += 1
    @konkurrancer.save

    respond_to do |format|
      format.js
    end
  end
end

这是我的模型:

class Konkurrancer < ActiveRecord::Base
  after_save :do_foobar

  private
    def do_foobar

      rating_score = self.rating_score
      ratings = self.ratings
      rating = (rating_score/ratings)
      self.update_attributes(:rating => rating)

    end
end

我的 Rails 日志:

Started POST "/konkurrancers/rate/46" for 127.0.0.1 at 2011-04-26 23:40:56 +0200

  Processing by KonkurrancersController#rate as */*
  Parameters: {"utf8"=>"Ô£ô", "authenticity_token"=>"MACFM37hX4S6XA9vryn7gtfl21P
vcaPBSiKDI8mfurg=", "vind"=>{"rating"=>"4"}, "id"=>"46"}
  ←[1m←[36mKonkurrancer Load (1.0ms)←[0m  ←[1mSELECT `konkurrancers`.* FROM `kon
kurrancers`←[0m
  ←[1m←[35mCACHE (0.0ms)←[0m  SELECT `konkurrancers`.* FROM `konkurrancers`
  ←[1m←[36mCACHE (0.0ms)←[0m  ←[1mSELECT `konkurrancers`.* FROM `konkurrancers`←
[0m
  ←[1m←[35mKonkurrancer Load (1.0ms)←[0m  SELECT `konkurrancers`.* FROM `konkurr
ancers` WHERE (`konkurrancers`.`cached_slug` = '46') LIMIT 1
  ←[1m←[36mSQL (2.0ms)←[0m  ←[1mSELECT sluggable_id FROM slugs WHERE ((slugs.slu
ggable_type = 'Konkurrancer' AND slugs.name = '46' AND slugs.sequence = 1))←[0m
  ←[1m←[35mKonkurrancer Load (1.0ms)←[0m  SELECT `konkurrancers`.* FROM `konkurr
ancers` WHERE (`konkurrancers`.`id` = 46) LIMIT 1
  ←[1m←[36mSQL (0.0ms)←[0m  ←[1mBEGIN←[0m
  ←[1m←[35mLink Load (1.0ms)←[0m  SELECT `links`.* FROM `links` WHERE (`links`.k
onkurrancer_id = 46) LIMIT 1
  ←[1m←[36mSQL (0.0ms)←[0m  ←[1mROLLBACK←[0m
Rendered konkurrancers/_rating.html.erb (1.0ms)
Rendered konkurrancers/rate.js.erb (22.0ms)
Completed 200 OK in 606ms (Views: 286.0ms | ActiveRecord: 6.0ms)

我应该如何创建这个?

【问题讨论】:

标签: ruby-on-rails ruby ruby-on-rails-3


【解决方案1】:

你想要的是一个回调。您可以在 Konkurrancer 模型上创建一个 after_save 回调,该回调在该模型调用 save() 方法后触发。

例如:

class Konkurrancer < ActiveRecord::Base
  after_save :do_foobar

  private
    def do_foobar

      rating_score = self.rating_score
      ratings = self.ratings
      rating = (rating_score/ratings)
      self.update_attributes(:ratings => rating)

    end
end

[EDIT]您应该使用self,因为您正在编辑的模型就是模型本身。对其进行测试,并应用必要的逻辑/实现。

查看guide 了解更多信息。

希望有帮助!

【讨论】:

  • 我如何确保更新的是正确的列?
  • 我用一个示例代码块更新了我的答案,据称它突出了您的问题。根据您的需要进行修改。
  • 我的评分列中没有保存任何值
  • 但这不是陷入无限循环的方式吗,因为 update_attributes 会再次调用 model.save 使回调再次触发?
  • 是的,Nozim,这将进入无休止的递归,并且永远不会真正起作用。正确的方法是按照 Zakelfassi 的回答使用 self.update_column
【解决方案2】:
  @konkurrancer.update_attributes :ratings=>'updated value'

【讨论】:

    【解决方案3】:

    看到这个after_save method

    【讨论】:

      【解决方案4】:

      after_save 回调中的任何 update_attribute 都会导致 Rails3+ 中的递归。 应该做的是:

      after_save :updater
      # Awesome Ruby code
      # ...
      # ...
      
      private
      
        def updater
          self.update_column(:column_name, new_value) # This will skip validation gracefully.
        end
      

      【讨论】:

        【解决方案5】:

        不知道为什么人们对错误答案投赞成票而对正确答案投反对票。

        对于 Rails 3 及更高版本,Zakelfassi 是对的,而 Christian Fazzini 是错的。如果您在保存回调中执行#update_attributes,您将进入无休止的递归。你想按照他的例子做#update_column。

        你自己试试,你会看到的。

        【讨论】:

        • 赞成票让我非常困惑,以至于我去检查了 Rails 源代码,对我的代码进行了广泛的单元测试,只是为了 100% 确定......
        • 是的,我只能假设他们使用的是旧版本的 Rails。
        • 请注意,Christian Fazzini 的原始问题和接受的答案都是 2011 年的。Rails 版本几乎肯定是 2.x。
        【解决方案6】:

        这篇文章中的许多答案可能是正确的,但现在已经过时了。 Rails docs 建议直接赋值 (self.attribute = 'value') 而不是在回调中更新或保存属性 (update(attribute: 'value')。

        after_save :do_foobar
        
        private
        
        def do_foobar
          self.attribute = "value"
        end
        

        不好 (.update_column)

        安全,因为它会阻止触发额外的回调,但不建议这样做,因为它可能会产生意想不到的副作用。

        after_save :do_foobar
        
        private
        
        def do_foobar
          update_column(:attribute, "value")
        end
        

        更糟 (.update)

        可以无限触发后续回调。

        after_save :do_foobar
        
        private
        
        def do_foobar
          update(attribute: "value")
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-22
          • 1970-01-01
          • 2016-09-23
          • 1970-01-01
          • 2019-12-17
          • 1970-01-01
          • 1970-01-01
          • 2023-04-03
          相关资源
          最近更新 更多