ruby jsonb数组元素唯一性的Rails验证

uqzxnwby  于 2023-05-06  发布在  Ruby
关注(0)|答案(1)|浏览(111)

我有一个作者模型和一本书模型。
一个作者可以有很多书,一本书也可以有很多作者。因此,它们通过第三个模型AuthorBook链接。
该模型有三个字段:author_id,book_id,还有:角色
Roles是一个jsonB数组,其中包含作者在本书中的角色。它可以是例如“main_author”、“corrector”、“translator”等。作者可以具有1个、多个或所有角色。
我想要一个AuthorBook,对于一本给定的书,只允许一个作者有一定的角色。只能有一个“main_author”、“corrector”等。
我试着实现一个验证,比如这个:

class AuthorBook < ApplicationRecord
  validate :uniqueness_of_author_role_for_book

  def uniqueness_of_author_role_for_book
    others_records = AuthorBook.where(book_id:).where.not(user_id:)

    return if others_records.blank?

    other_records.each do |record|
      errors.add(:roles, "#{record.roles.join(',')} already exists on another user") if roles_already_assigned?(record)
    end
  end

  private

  def roles_already_assigned?(record)
    roles.map(&:to_s).intersect?(record.roles)
  end
end

可悲的是,有一个边缘情况下,这不工作。
如果我将AuthorBook的作者从author 1更新为author 2,具有相同的角色;这段代码将检测author 1的存在,author 1有一本书的AuthorBook,具有相同角色,验证将失败。
它不应该失败,因为在更新之后,author 1将不再具有该角色,并且它应该是有效的。
可能有更好的方法来做到这一点,但我似乎不能指出它。

fquxozlt

fquxozlt1#

另一种方法是为每个角色设置一条AuthorBook记录,而不是数组列。然后,您可以简单地在book_id上使用标准的唯一性验证

class AuthorBook
  validates :role, uniqueness: { scope: :book_id }

相关问题