有关如何使用has_many:through关系正确设置验证的指导?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有关如何使用has_many:through关系正确设置验证的指导?相关的知识,希望对你有一定的参考价值。
我设置了三个模型:User,List和UserList - 后者是用户和List之间的连接模型,采用has_many_through
关系。
我正在尝试设置我认为应该是相当普遍的唯一性约束 - 但它并不是很有效。非常感谢您的指导/建议!
技术细节
我有3个型号:
class User < ApplicationRecord
has_many :user_lists
has_many :lists, through: :user_lists, dependent: :destroy
End
class List < ApplicationRecord
has_many :user_lists
has_many :users, through: :user_lists, dependent: :destroy
# no duplicate titles in the List table
validates :title, uniqueness: true
End
class UserList < ApplicationRecord
belongs_to :list
belongs_to :user
# a given user can only have one copy of a list item
validates :list_id, uniqueness: { scope: :user_id }
end
正如您所看到的,我希望列表项基于其标题是唯一的。换句话说,如果用户Adam添加了标题为“黑暗骑士”的列表,那么用户Beatrice添加标题为“黑暗骑士”的列表实际上不应该创建新的列表记录 - 它应该只创建一个新的/不同的UserList关联,指向先前创建的List项。
(有点切向,但我也在桌子上添加了一个独特的索引,因为我明白这可以避免竞争条件)
class AddIndexToUserLists < ActiveRecord::Migration[5.2]
def change
add_index :user_lists, [:user_id, :list_id], unique: true
end
end
这里出了问题。
作为用户Adam,我登录,并将新标题“The Dark Knight”添加到我的列表中。
这是控制器动作(假设current_user正确检索Adam):
# POST /lists
def create
@list = current_user.lists.find_or_create_by!(list_params)
end
这正确地导致创建新的List记录和关联的UserList记录。欢呼!
作为亚当,如果我尝试将同样的标题“黑暗骑士”添加到我的列表中,则没有任何反应 - 包括控制台上没有错误。欢呼!
然而 - 作为用户Beatrice,如果我登录并且现在尝试将“The Dark Knight”添加到我的列表中,我现在在控制台中出现错误:
POST http://localhost:3000/api/v1/lists 422 (Unprocessable Entity)
我的调试和假设
如果我删除List.title上的唯一性约束,则此错误消失,并且Beatrice能够将“黑暗骑士”添加到她的列表中。
然而,List然后包含两个标题为“黑暗骑士”的记录,这似乎是多余的。
作为亚当,似乎current_user.lists.find_or_create_by!(list_params)
在我的控制器动作中找到了与我当前用户相关的现有“黑暗骑士”列表,并意识到它存在 - 从而不会触发创建动作。
然后作为比阿特丽斯,似乎相同的控制器动作没有找到与我当前用户相关联的现有“黑暗骑士”列表项 - 因此它尝试触发创建动作。
但是,此创建操作尝试创建一个新的List项,其标题已存在 - 即它与List.rb模型唯一性验证相违背。
我不确定如何修改find_or_create_by操作或模型验证,以确保为Beatrice创建新的UserList记录/关联 - 但不是新的List记录(因为已经存在)。
感觉就像我在这里错过了一些简单的东西。或者可能不是。非常感谢有关如何进行的一些指导。谢谢!
我99%肯定发生的事情是current_user.lists.find_or_create_by
只会搜索用户有UserList条目的List记录。因此,如果List存在但当前用户没有关联,它将尝试创建一个与现有列表冲突的新列表。
假设这是问题,您需要独立于用户关联找到List:@list = List.find_or_create_by(list_params)
获得该列表后,可以通过关联或UserList模型创建UserList记录。如果您正在寻找简洁,我认为您可以使用current_user.lists << @list
来创建UserList,但是如果用户已经拥有该列表的UserList,您应该检查它的行为,我不确定它是否会覆盖您现有的数据。
所以(假设<<方法适用于创建UserList),您的控制器操作可能如下所示:
def create
@list = List.find_or_create_by!(list_params)
current_user.lists << @list
end
以上是关于有关如何使用has_many:through关系正确设置验证的指导?的主要内容,如果未能解决你的问题,请参考以下文章
何时在 Rails 中使用“has_many :through”关系?
如何避免 has_many :through 关系中的重复?