时间:2017-06-13 19:42:25

标签: ruby-on-rails activerecord

我的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 [:license]

更新licence_type列

为此目的使用rescue ActiveRecord::RecordNotUnique

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

2 - 除了ActiveRecord::RecordNotUnique之外我怎么能捕捉到其他任何异常/错误?我想在底部添加另一个rescue Exception => e,所以我可以捕获所有其他异常,但我想我在某个地方读到了捕捉一般异常这样的不好并且我应该使用像rescue => e这样的东西?

感谢任何代码段,谢谢!

1 个答案:

答案 0 :(得分: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,这将无效。