提问人:Samuel-Zacharie Faure 提问时间:5/3/2023 更新时间:5/3/2023 访问量:125
对 jsonb 数组元素唯一性的 Rails 验证
Rails validation on jsonb array element uniqueness
问:
我有一个 Author 模型和一个 Book 模型。
一个作者可以有很多书,一本书也可以有很多作者。因此,它们通过第三个模型 AuthorBook 链接。
该模型有三个字段:author_id、book_id,还有:角色。
Roles 是一个 jsonB 数组,包含该作者相对于本书的角色。例如,它可以是“main_author”、“校正器”、“翻译器”等。作者可以有 1 个、多个或所有角色。
我想要一本 AuthorBook,对于一本给定的书,只允许一个作者担任特定角色。只能有一个“main_author”、“校正器”等。
我尝试实现如下验证:
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 的作者从 author1 更新为 author2,角色相同;这段代码将检测 author1 的存在,该 author1 具有与此完全相同角色的本书的 AuthorBook,并且验证将失败。
它不应该失败,因为在更新后,author1 将不再具有该角色,并且它应该有效。
可能有更好的方法可以做到这一点,但我似乎无法确定它。
答:
2赞
Arctodus
5/3/2023
#1
另一种方法是为每个角色使用一条记录,而不是数组列。然后,您可以简单地使用标准唯一性验证,作用域AuthorBook
book_id
class AuthorBook
validates :role, uniqueness: { scope: :book_id }
评论
0赞
Samuel-Zacharie Faure
5/3/2023
这是一个有趣的命题。我需要将类的名称更改为不太传统的名称,例如“AuthorBookWithRole”(欢迎提出建议)以传达我们有一个独特角色的想法,但我想它可以工作。如果没有更好的建议,会将其标记为已接受的答案。
0赞
Samuel-Zacharie Faure
5/3/2023
经过一番思考,这可能是最好的方法。
评论