Rails 关于 hook method 的问题

shangrenzhidao · 2014年11月05日 · 最后由 shangrenzhidao 回复于 2014年11月06日 · 2090 次阅读

直接上代码:

def destroy
   begin
     @user.destroy   
     flash[:notice] = "User #{@user.name} deleted"
     if User.count.zero?   # 1
       raise 'cant delete last user' # 2
     end # 3
   rescue StandardError => e
     flash[:notice] = e.message
   end
   respond_to do |format|
     format.html { redirect_to users_url }
     format.json { head :no_content }
   end
 end
class User < ActiveRecord::Base
   after_destroy :ensure_an_admin_remains # 4
  validates :name, presence: true, uniqueness: true
  has_secure_password

  private
    def ensure_an_admin_remains
      if User.count.zero?
        raise "Cant delete last user"
      end
    end
end

代码 1 2 3 是我后加上的,我的问题是,如果我不用 4 这里的 hook, 使用 1 2 3 做一个判断为什么不行?

首先你的逻辑有问题吧,删除了再判断,然后来一个cant delete last user!!! 其次:

def destroy
   begin
     @user.destroy   
     flash[:notice] = "User #{@user.name} deleted"
     if User.count.zero?   # 1 如果这里确实为ture
       raise 'cant delete last user' # 2 那么这里异常会调到rescue
     end # 3
   rescue StandardError => e # rescue Exception => e
     flash[:notice] = e.message # 那么这里就会执行
   end
   respond_to do |format| # 正常执行
     format.html { redirect_to users_url }
     format.json { head :no_content }
   end
 end

我猜测是不是 StandardError 的问题,报什么错你没说。换顶层的Exception => e试下

#1 楼 @flowerwrong 还是不行! 我看了一下 log

Processing by UsersController#destroy as HTML
  Parameters: {"authenticity_token"=>"BcbWgCZ+jFBcIKQNHO7z37txj/XYdkVMrXle/ZhPTcA=", "id"=>"3"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", "3"]]
   (0.1ms)  begin transaction
  SQL (0.3ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 3]]
   (3.2ms)  commit transaction
   (0.2ms)  SELECT COUNT(*) FROM "users"
Redirected to http://localhost:3000/users
Completed 302 Found in 8ms (ActiveRecord: 4.0ms)

我猜原因是这样的,既然在 transaction 中,那么执行删除并没有真正从数据库中清除数据,所以不满足 if 判断,所以 if 是句废话。我觉得非要这么做的话,等事务结束了,在把删除的数据插回去,但是好像比较二,而且 id 也会改变。

那些钩子方法全部在事务里面,出问题会回滚,对于第一块代码,有两种方法 第一种是改变逻辑,先判断是不是为 1 第二种是把 begin 和 rescue 之间的代码写在事务里面User.transaction do ... end

#3 楼 @flowerwrong 有道理,还是老老实实用 hook 吧,人家提供好了,非要自己给自己找麻烦。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号