更新变更集时出现复合唯一约束错误
composite unique constraint error while updating the changeset
我有一个架构 two_fa_details,其中 answer 和 question_id 是字段,两者都是唯一的。
现在,当我尝试首先将数据插入其中时,它被插入但下次更新它时不起作用..
它说约束错误。
我有一个函数 set_two_factor_details 用于更新 table..
该函数在第一次插入数据时工作正常..但是当我更新它时...它不起作用..我有一个用于此函数的 PUT API。
这是我的模式迁移文件 two_fa_details
def change do
create table(:two_fa_details) do
add :answer, :string
add :userprofile_id, references(:user_profile, on_delete: :nothing)
add :question_id, references(:questions, on_delete: :nothing)
timestamps()
end
create index(:two_fa_details, [:userprofile_id])
create index(:two_fa_details, [:question_id])
create unique_index(:two_fa_details, [:userprofile_id, :question_id], name: :user_twofa_detail)
end
这是一段代码
def set_twofactor_details(client_id, twofa_records) do
user = Repo.get_by(UserProfile, client_id: client_id)
twofa_records = Enum.map(twofa_records, &get_twofa_record_map/1)
Enum.map(twofa_records, fn twofa_record ->
Ecto.build_assoc(user, :two_fa_details)
|> TwoFaDetails.changeset(twofa_record)
end)
|> Enum.zip(0..Enum.count(twofa_records))
|> Enum.reduce(Ecto.Multi.new(), fn {record, id}, acc ->
Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record)
end)|>IO.inspect()
|> Ecto.Multi.update(
:update_user,
Ecto.Changeset.change(user, two_factor_authentication: true, force_reset_twofa: false)
)
|> Repo.transaction()|>IO.inspect()
|> case do
{:ok, _} ->
{:ok, :updated}
{:error, _, changeset, _} ->
error_string = get_first_changeset_error(changeset)
Logger.error("Error while updating TWOFA: #{error_string}")
{:error, 41001, error_string}
end
end
输出应该基本上更新 table 并返回两个 fa 详细信息更新消息。
但在日志中它显示的约束 error.please 帮助我解决这个问题..我是长生不老药的新手。
{:error, :twfa_record_0,
#Ecto.Changeset<
action: :insert,
changes: %{answer: "a", question_id: 1, userprofile_id: 1},
errors: [
unique_user_twofa_record: {"has already been taken",
[constraint: :unique, constraint_name: "user_twofa_detail"]}
],
data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
valid?: false
>, %{}}
[error] Error while updating TWOFA: `unique_user_twofa_record` has already been taken
您写道:
the output should be basically updating the table and returning two fa details updated message.
但是代码returns:
#Ecto.Changeset<
action: :insert,
changes: %{answer: "a", question_id: 1, userprofile_id: 1},
errors: [
unique_user_twofa_record: {"has already been taken",
[constraint: :unique, constraint_name: "user_twofa_detail"]}
],
data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
valid?: false
>
看看它是怎么说的 action: :insert
。所以你不是更新,而是插入,这就解释了错误。
insert_or_update
只会更新一条记录,前提是该记录是从数据库加载的。在您的代码中,您是从头开始构建记录,因此它们将始终是一个插入。在将它们传递给变更集之前,您需要使用 Repo.get
或类似的方法来获取它们,以便您最终可以调用 insert_or_update
.
我尝试使用 upserts
来实现 ecto
它奏效了。
这是一段代码供参考
Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record,
on_conflict: :replace_all_except_primary_key,
conflict_target: [:userprofile_id, :question_id] )
我有一个架构 two_fa_details,其中 answer 和 question_id 是字段,两者都是唯一的。 现在,当我尝试首先将数据插入其中时,它被插入但下次更新它时不起作用.. 它说约束错误。
我有一个函数 set_two_factor_details 用于更新 table.. 该函数在第一次插入数据时工作正常..但是当我更新它时...它不起作用..我有一个用于此函数的 PUT API。 这是我的模式迁移文件 two_fa_details
def change do
create table(:two_fa_details) do
add :answer, :string
add :userprofile_id, references(:user_profile, on_delete: :nothing)
add :question_id, references(:questions, on_delete: :nothing)
timestamps()
end
create index(:two_fa_details, [:userprofile_id])
create index(:two_fa_details, [:question_id])
create unique_index(:two_fa_details, [:userprofile_id, :question_id], name: :user_twofa_detail)
end
这是一段代码
def set_twofactor_details(client_id, twofa_records) do
user = Repo.get_by(UserProfile, client_id: client_id)
twofa_records = Enum.map(twofa_records, &get_twofa_record_map/1)
Enum.map(twofa_records, fn twofa_record ->
Ecto.build_assoc(user, :two_fa_details)
|> TwoFaDetails.changeset(twofa_record)
end)
|> Enum.zip(0..Enum.count(twofa_records))
|> Enum.reduce(Ecto.Multi.new(), fn {record, id}, acc ->
Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record)
end)|>IO.inspect()
|> Ecto.Multi.update(
:update_user,
Ecto.Changeset.change(user, two_factor_authentication: true, force_reset_twofa: false)
)
|> Repo.transaction()|>IO.inspect()
|> case do
{:ok, _} ->
{:ok, :updated}
{:error, _, changeset, _} ->
error_string = get_first_changeset_error(changeset)
Logger.error("Error while updating TWOFA: #{error_string}")
{:error, 41001, error_string}
end
end
输出应该基本上更新 table 并返回两个 fa 详细信息更新消息。 但在日志中它显示的约束 error.please 帮助我解决这个问题..我是长生不老药的新手。
{:error, :twfa_record_0,
#Ecto.Changeset<
action: :insert,
changes: %{answer: "a", question_id: 1, userprofile_id: 1},
errors: [
unique_user_twofa_record: {"has already been taken",
[constraint: :unique, constraint_name: "user_twofa_detail"]}
],
data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
valid?: false
>, %{}}
[error] Error while updating TWOFA: `unique_user_twofa_record` has already been taken
您写道:
the output should be basically updating the table and returning two fa details updated message.
但是代码returns:
#Ecto.Changeset<
action: :insert,
changes: %{answer: "a", question_id: 1, userprofile_id: 1},
errors: [
unique_user_twofa_record: {"has already been taken",
[constraint: :unique, constraint_name: "user_twofa_detail"]}
],
data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
valid?: false
>
看看它是怎么说的 action: :insert
。所以你不是更新,而是插入,这就解释了错误。
insert_or_update
只会更新一条记录,前提是该记录是从数据库加载的。在您的代码中,您是从头开始构建记录,因此它们将始终是一个插入。在将它们传递给变更集之前,您需要使用 Repo.get
或类似的方法来获取它们,以便您最终可以调用 insert_or_update
.
我尝试使用 upserts
来实现 ecto
它奏效了。
这是一段代码供参考
Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record,
on_conflict: :replace_all_except_primary_key,
conflict_target: [:userprofile_id, :question_id] )