使用 Ecto (MySQL) 进行 upsert 的最简单方法是什么
What is the simplest way to do upsert with Ecto (MySQL)
upsert 在我的应用程序中很常见,我想采用最干净、最简单的方式来实现 upsert。
- 我应该使用片段来实现原生 sql upsert 吗?
- 有什么惯用的 ecto 方式来做 upsert 吗?
您可以使用 Ecto.Repo.insert_or_update/2,请注意,要使其正常工作,您必须从数据库中加载现有模型。
model = %Post{id: 'existing_id', ...}
MyRepo.insert_or_update changeset
# => {:error, "id already exists"}
示例:
result =
case MyRepo.get(Post, id) do
nil -> %Post{id: id} # Post not found, we build one
post -> post # Post exists, using it
end
|> Post.changeset(changes)
|> MyRepo.insert_or_update
case result do
{:ok, model} -> # Inserted or updated with success
{:error, changeset} -> # Something went wrong
end
如果您希望通过 id
以外的内容进行更新,您可以将 get_by
换成 get
,如下所示:
model = %User{email: "existing_or_new_email@heisenberg.net", name: "Cat", ...}
model |> User.upsert_by(:email)
# => {:found, %User{...}} || {:ok, %User{...}}
defmodule App.User do
alias App.{Repo, User}
def upsert_by(%User{} = record_struct, selector) do
case User |> Repo.get_by({selector, record_struct |> Map.get(selector)}) do
nil -> %User{} # build new user struct
user -> user # pass through existing user struct
end
|> User.changeset(record_struct |> Map.from_struct)
|> Repo.insert_or_update
end
end
如果您正在寻找一种适用于跨模型和多个选择器(即国家/地区 + 护照号码)的灵活方法,请查看我的十六进制包 EctoConditionals!
在我的例子中 insert_or_update
由于唯一索引约束
引发了错误
对我有用的是 Postgres v9.5 通过 on_conflict
参数更新插入:
(考虑到唯一列称为 user_id
)
changeset
|> MyRepo.insert(
on_conflict: :replace_all,
conflict_target: :user_id
)
upsert 在我的应用程序中很常见,我想采用最干净、最简单的方式来实现 upsert。
- 我应该使用片段来实现原生 sql upsert 吗?
- 有什么惯用的 ecto 方式来做 upsert 吗?
您可以使用 Ecto.Repo.insert_or_update/2,请注意,要使其正常工作,您必须从数据库中加载现有模型。
model = %Post{id: 'existing_id', ...}
MyRepo.insert_or_update changeset
# => {:error, "id already exists"}
示例:
result =
case MyRepo.get(Post, id) do
nil -> %Post{id: id} # Post not found, we build one
post -> post # Post exists, using it
end
|> Post.changeset(changes)
|> MyRepo.insert_or_update
case result do
{:ok, model} -> # Inserted or updated with success
{:error, changeset} -> # Something went wrong
end
如果您希望通过 id
以外的内容进行更新,您可以将 get_by
换成 get
,如下所示:
model = %User{email: "existing_or_new_email@heisenberg.net", name: "Cat", ...}
model |> User.upsert_by(:email)
# => {:found, %User{...}} || {:ok, %User{...}}
defmodule App.User do
alias App.{Repo, User}
def upsert_by(%User{} = record_struct, selector) do
case User |> Repo.get_by({selector, record_struct |> Map.get(selector)}) do
nil -> %User{} # build new user struct
user -> user # pass through existing user struct
end
|> User.changeset(record_struct |> Map.from_struct)
|> Repo.insert_or_update
end
end
如果您正在寻找一种适用于跨模型和多个选择器(即国家/地区 + 护照号码)的灵活方法,请查看我的十六进制包 EctoConditionals!
在我的例子中 insert_or_update
由于唯一索引约束
对我有用的是 Postgres v9.5 通过 on_conflict
参数更新插入:
(考虑到唯一列称为 user_id
)
changeset
|> MyRepo.insert(
on_conflict: :replace_all,
conflict_target: :user_id
)