我已经建立了三个模型:User,List和UserList,后者是has_many_through
关系中User和List之间的联接模型。
我正在尝试设置我认为应该是非常原始的唯一性约束-但这并不奏效。请感谢您的指导/建议!
我有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添加了标题为“黑暗骑士”的列表实际上不应该创建新的List记录-它应该创建一个新的/与众不同的UserList关联,指向先前创建的列表项。
(有些切线,但是我也在表上添加了唯一索引,因为我知道这避免了竞争情况)
class AddIndexToUserLists < ActiveRecord::Migration[5.2]
def change
add_index :user_lists, [:user_id, :list_id], unique: true
end
end
以亚当用户的身份登录,并将新标题“黑暗骑士”添加到我的列表中。
这是控制器操作(假设current_user正确检索了Adam):
# POST /lists
def create
@list = current_user.lists.find_or_create_by!(list_params)
end
这将正确地导致创建新的列表记录和关联的用户列表记录。哇!
作为亚当,如果我尝试将相同的标题“黑暗骑士”再次添加到我的列表中,则什么都没有发生-包括控制台上没有错误。哇!
但是-作为Beatrice用户,如果我登录并现在尝试将“黑暗骑士”添加到我的列表中,则现在在控制台中出现错误:
POST http://localhost:3000/api/v1/lists 422 (Unprocessable Entity)
如果我删除List.title上的唯一性约束,此错误将消失,并且Beatrice可以将“黑暗骑士”添加到她的列表中。
但是,List然后包含两个记录,两个记录都称为“黑暗骑士”,这似乎是多余的。
作为亚当,看来我的控制器操作中的current_user.lists.find_or_create_by!(list_params)
正在查找与我的当前用户关联的现有“黑暗骑士”列表,并意识到它存在-从而不会触发创建操作。
然后作为Beatrice,似乎相同的控制器操作未找到与我的当前用户相关联的现有“黑暗骑士”列表项,因此它试图触发create动作。
但是,此创建操作尝试创建一个具有已经存在的标题的新List项,即,它违反了List.rb模型唯一性验证。
我不确定如何修改find_or_create_by操作或模型验证,以确保为Beatrice创建新的UserList记录/关联-但不会创建新的List记录(因为已经存在)。 / p>
感觉好像我在这里错过了一些简单的事情。或者可能不是。非常感谢您提供有关如何进行操作的指导。谢谢!
答案 0 :(得分:0)
我有99%的把握是current_user.lists.find_or_create_by
只会搜索用户具有UserList条目的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