【问题标题】:Catch ActiveRecord::RecordNotUnique and all other exceptions after捕获 ActiveRecord::RecordNotUnique 和之后的所有其他异常
【发布时间】:2017-06-14 08:41:24
【问题描述】:

我的 LineItem 模型中有一个唯一性约束,如下所示:

class LineItem < ApplicationRecord
    # this add uniqueness validation to [cart_id + product_id] together
    validates :cart_id, uniqueness: { scope: [:product_id] }
end

为了提高安全性,我还为数据库中的这些列添加了索引 + unique:true

在我的LineItemsController#create 我有以下内容

class LineItemsController < ApplicationController

    def create 

        @cart = Cart.find(session[:cart_id]

        product = Product.find(params[:product_id]

        @line_item = @cart.add_product(product, params[:licence])

        respond_to do |format|

            @line_item.save!

            format.html { redirect_to products_url }
            format.js 

         rescue ActiveRecord::RecordNotUnique

          @cart.line_items
             .find_by(product_id: params[:product_id])
             .update(licence_type: params[:licence], price: product.price)

          format.js

        end

    end

end

我想要做的是:如果用户添加一个已经具有相同 product_id 和 cart_id 的行项目,则使用 params[:licence] 更新 licence_type 列

我为此使用rescue ActiveRecord::RecordNotUnique

1 - 这是一个好方法吗(这样我可以为自己保存一个额外的请求,每次检查数据库中是否存在记录)?

2 - 我如何捕捉除 ActiveRecord::RecordNotUnique 之外的任何其他异常/错误?我想在底部添加另一个rescue Exception =&gt; e,这样我就可以捕获所有其他异常,但我想我在某处读到这样的一般异常并不好,我应该改用rescue =&gt; e 之类的东西?

感谢任何代码 sn-p,谢谢!

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    Never rely on uniqueness validation 让您免于重复数据。它对竞争条件有一个公认的弱点。

    在这种情况下,您可能想要的是find_or_initialize_by

    line_item = @cart.line_items.find_or_initialize_by(product_id: params[:product_id])
    line_item.license_type = params[:license]
    line_item.save
    

    除此之外,您应该将此数据的唯一性约束移动到数据库中。具体的做法取决于你的数据库是什么。

    保留验证器是可以的,但把它当作前端验证;有作为一种方便,创造更好的用户体验。但不要依赖它来保持数据的一致性,这是数据库的工作。


    对代码示例的另一种评论:

    rescue 必须放置在 begin..end 块内或作为方法体的一部分。但在您的情况下,您将它放在 do..end 块内,而周围没有 begin..end,这是行不通的。

    【讨论】:

    • 感谢您的详细回答,我在帖子中已经提到我已将索引添加到我的数据库级别。我完全同意你的观点,但让我有点困扰的是,实际上我总共有 4 个请求来创建该行项目,我更新了我的创建方法以更加清晰:首先我检索购物车,其次我通过 id 找到产品(得到它的价格),第三我检查产品是否已经存在于购物车中,第四我保存我的记录
    • 我的想法是我可以将请求的数量减少到 3 个甚至更少,所以我尝试捕获 ActiveRecord::RecordNotUnique,我发现它仅在数据库唯一性约束失败时触发(而不是当模型验证失败时),所以我想也许我可以从模型中删除验证,只依靠数据库来引发异常,这样我就可以用ActiveRecord::RecordNotUnique
    • 这至少允许跳过每次检查数据库中是否存在记录的请求。但我想知道您是否有更好的想法或想法?
    • @medBo 多个请求如果速度很快,就必然不好。您是否通过分析验证了这是一个性能问题?在这种情况下,假设您的购物车通常没有大量的 line_items,我会提前预先加载产品和 line_items。
    • 不,我没有测量性能我只是尽量减少我所有开发中的请求数量:) 我总是有这样的感觉,即更多的请求意味着更多的“慢”更多“坏用户”经验”,但我会使用find_or_initialize_by,它使我的代码比之前更干净:D(急于加载 line_items 在我的情况下不会有任何好处,因为它无论如何都会提出额外的请求,而产品不能急于加载,因为我通过 id 检索它以查看其价格并将该价格放入订单项中)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    相关资源
    最近更新 更多