【问题标题】:How to validate uniqueness of a field without saving in Rails?如何在不保存在 Rails 中的情况下验证字段的唯一性?
【发布时间】:2016-07-19 23:45:29
【问题描述】:

假设你有User has_many Books。每本书都有一个名称字段。

用户输入他们的书籍,然后将其作为名称数组提交给应用程序。名称数组将替换任何现有书籍。

如果更新失败,则不应更改书籍。

class Book
  belongs_to :user

  validates_uniquness_of :name, scope: [:user]

如何在不保存的情况下检查每本书的有效性?

例如:

['Rails Guide', 'Javascript for Dummies'] 将是有效的。

['Javascript for Dummies', 'Javascript for Dummies'] 无效。

params[:books].each{| b | Book.new(b).valid? } 将不起作用,因为必须保存该书才能获得唯一性。

Mongoid

【问题讨论】:

    标签: ruby-on-rails validation mongoid


    【解决方案1】:

    您可以使用Active Record Transaction。启动事务,调用save,如果失败则回滚整个事务。例如:

    Book.transaction do
        params[:books].each{ |b| Book.new(b).save! }
    end
    

    如果出现异常,整个事务将被中止。你应该通过捕捉ActiveRecord::RecordInvalid来处理这种情况。

    【讨论】:

    • 您还需要数据库中的唯一性约束,然后您还需要捕获ActiveRecord::RecordNotUnique
    • 这是一个很好的方法,但是 Mongo 没有事务。
    【解决方案2】:

    您可以使用Array#map 将书籍属性数组转换为书籍名称数组。然后使用Array#uniq从书籍名称数组中删除重复项,然后检查结果数组是否与原始书籍属性数组的大小相同:

    are_books_uniq = params[:books].map{|b| b[:name]}.uniq.size == params[:books].size
    

    这样您就可以在不接触数据库的情况下执行检查。但为了安全起见,您应该将所有账簿保存在一个事务中(参见 @Aaron 的 answer)。

    【讨论】:

    • 我不喜欢这种方式,因为它重复了验证逻辑。
    【解决方案3】:

    事实证明这比我想象的要复杂得多。

    我想出的解决方案如下:

      def update params
        names = params.delete( :books )
    
        new_books = names.map{| title | Book.new( name:name )}
        validate_books_for new_books 
    
        return false if errors.present?
        return false unless super( params )
    
        self.books = new_books
    
        self
      end
    

    大部分复杂性来自两个模型的耦合。我明白为什么耦合模型不是一个好主意。也许更好的设计是将书籍存储为数组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      相关资源
      最近更新 更多