What is the easiest way to deal with Ecto (MySQL)

Running upsert is common in my application, and I want to implement the simplest and easiest way to implement upsert.

  • Should I use fragments to implement native sql upsert?
  • Any idiomatic ecto way to do upsert?
+6
source share
2 answers

You can use Ecto.Repo.insert_or_update / 2 , note that for this you will have to load existing models from the database.

model = %Post{id: 'existing_id', ...} MyRepo.insert_or_update changeset # => {:error, "id already exists"} 

Example:

 result = case MyRepo.get(Post, id) do nil -> %Post{id: id} # Post not found, we build one post -> post # Post exists, let use 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 
+13
source

If you want to improve something other than id , you can swap get_by with get as follows:

 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 

In case of unforeseen circumstances, you are looking for a flexible approach that works in different models and several selectors (for example, country + passport number), check out my hex EctoConditionals package!

+2
source

All Articles