【问题标题】:How to avoid UniqueViolation errors如何避免 UniqueViolation 错误
【发布时间】:2018-01-22 13:08:45
【问题描述】:

我的类似 facebook 的应用程序让用户有机会删除来自关注用户的不需要的消息。为此,我在用户和微博之间创建了MicropostQuarantine 关联:如果用户和微博之间存在这种关联,那么该用户将不会在他们的提要中看到该微博。但是,微博也会在用户的个人资料页面中可见,包括那些已经被隔离的。因此,如果我从提要中删除了一条微博并访问了该微博用户的个人资料页面,我仍然会看到该微博并可以访问会引发以下错误的删除按钮:

ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_micropost_quarantines_on_user_id_and_micropost_id"
DETAIL:  Key (user_id, micropost_id)=(1, 300) already exists.
: INSERT INTO "micropost_quarantines" ("user_id", "micropost_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"):

微博控制器中的隔离方法很简单:

def quarantine
  current_user.micropost_quarantines.create!(micropost_id: @micropost.id)
end

有一个before_action :entitled_user, only: :quarantine定义如下:

def entitled_user
  @micropost = Micropost.find(params[:id])
  redirect_to root_url unless (current_user == @micropost.user || current_user.following.include?(@micropost.user))
end

如您所见,用户只能将自己的微博和微博与关注的用户隔离开来。为了避免 UniqueViolation 错误,我想到了在entitled_user 方法中添加一些代码,以检查关联是否已经存在:

def entitled_user
  @micropost = Micropost.find(params[:id])
  @quarantine = MicropostQuarantine.find_by(micropost_id: @micropost.id, user_id: current_user.id)
  redirect_to root_url unless (current_user == @micropost.user || current_user.following.include?(@micropost.user) || @quarantine.nil?)
end    

但这不起作用:entitled_user 方法由于某些未知原因被 rails 忽略/绕过,我不断从 ActiveRecord 接收 UniqueViolation: ERROR 并且整个 unless 条件被忽略,这样我就可以隔离非关注用户的微博。

【问题讨论】:

    标签: ruby-on-rails activerecord associations


    【解决方案1】:

    我认为我们不应该在复杂的情况下使用unless,请尝试以下操作:

    redirect_to root_url if (current_user != @micropost.user && current_user.following.exclude?(@micropost.user)) || @quarantine.present?
    

    提示:

    def entitled_user
      @micropost = Micropost.find(params[:id])
    
      if (current_user.id != @micropost.user_id && !current_user.following.exists?(@micropost.user_id)) ||
         current_user.micropost_quarantines.exists?(micropost_id: @micropost.id)
    
        redirect_to root_path and return
      end
    end
    
    • 使用:exists?(SQL 端)比:include?(Ruby 端)更高效
    • 使用@micropost.user_id而不是@micropost.user,因为我们不需要实例@user,所以我们不需要这样做:

    SELECT (*) FROM users WHERE id = #{@micropost.user_id}

    希望这会有所帮助!

    【讨论】:

    • 尝试将debugger放入方法entitled_user
    • 使用您的解决方案,服务器日志显示Filter chain halted as :entitled_user rendered or redirected
    • 我的 entitled_user 的原始版本可以工作,但没有检查微博是否已被隔离的代码部分。当我添加它时,一切都会崩溃。
    • 我不知道为什么,但据我了解,Rails 在处理 3 个条件句时似乎存在问题,所以我保留了 entitled_user 并尝试为第三个条件句创建一个特定的 before_action,现在它可以工作了。
    • 我明白了。请检查我的最终答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 2018-07-04
    • 1970-01-01
    • 2014-06-03
    • 2010-11-17
    • 2023-03-18
    • 1970-01-01
    相关资源
    最近更新 更多