dfalling
How to manage associations during creates/updates?
I have a schema Element with a has_many relationship to Photo. I want to manage this relationship when creating/updating an Element. I persist the order of the Photos in an order field in the join table. Right now I do all this manually with a lot more code than I’d like.
- Load Photos from changeset, to ensure they all exist and belong to the user (privileges)
- Create or update Element in an Ecto.Multi
- Manually create the ElementPhoto schemas after comparing with existing ElementPhotos
Is there a cleaner way to do this? I looked into Ecto.Changeset.put_assoc, but it 1) doesn’t generate IDs for my join table (I use UUIDs), and 2) doesn’t seem to provide a way to update the order field.
def create_user_element(attrs, user_id) do
changeset =
%Element{user_id: user_id}
|> Element.changeset(attrs)
|> validate_element_photos(user_id)
Multi.new()
|> Multi.insert(:element, changeset)
|> Multi.merge(fn %{element: element} ->
new_photo_ids = Ecto.Changeset.get_field(changeset, :photo_ids)
element_photos_multi(element.id, [], new_photo_ids)
end)
|> Repo.transaction()
|> case do
{:ok, %{element: element}} -> {:ok, element}
{:error, _field, %Ecto.Changeset{} = changeset, _} -> {:error, changeset}
end
end
defp validate_element_photos(changeset, user_id) do
photo_ids = Ecto.Changeset.get_field(changeset, :photo_ids, [])
photos =
Photo
|> where([photo], photo.id in ^photo_ids and photo.user_id == ^user_id)
|> Repo.all()
found_ids = Enum.map(photos, & &1.id)
missing_ids = photo_ids -- found_ids
case missing_ids do
[] ->
changeset
missing_ids ->
Enum.reduce(missing_ids, changeset, fn missing_id, changeset ->
Ecto.Changeset.add_error(
changeset,
:photo_ids,
"Photo #{missing_id} not found"
)
end)
end
end
defp element_photos_multi(element_id, existing_photo_ids, new_photo_ids) do
removed_photo_ids = existing_photo_ids -- new_photo_ids
added_photo_ids = new_photo_ids -- existing_photo_ids
multi = Multi.new()
case {removed_photo_ids, added_photo_ids} do
{[], []} ->
# no-op, no changes
multi
_ ->
# delete all previous items (easier than trying to rearrange them)
multi =
multi
|> Multi.delete_all(
:delete_photos,
from(p in ElementPhoto,
where: p.element_id == ^element_id
)
)
# add all items in proper order
new_photo_ids
|> Enum.with_index()
|> Enum.reduce(multi, fn {photo_id, index}, multi ->
changeset =
%ElementPhoto{}
|> ElementPhoto.changeset(%{element_id: element_id, photo_id: photo_id, order: index})
multi
|> Multi.insert("photo #{Integer.to_string(index)}", changeset)
end)
end
end
Thanks!
Most Liked
moogle19
Yes, validate_required for the foreign_keys doesn’t work with cast_assoc.
Take a look at the documentation.
You can specify required: true as an options in cast_assoc.
2
Popular in Questions
Hi All,
I set a environment variables in dev.exs , like below code.
when i start server, how can i set the ${enable} value?
thanks.
d...
New
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible.
total = 10
while total != 0
...
New
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
For example for a current url like
http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2,
I would like to get:
...
New
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Hello, how can I check the Phoenix version ?
Thanks !
New
Hi all,
I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage.
I’m trying to use Postgres...
New
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set?
Thanks.
New
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors:
[WARN] - (starship::utils): Executing command ...
New
Other popular topics
Hi All,
I set a environment variables in dev.exs , like below code.
when i start server, how can i set the ${enable} value?
thanks.
d...
New
Hi,
I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
Phoenix 1.4.0 released
Phoenix 1.4 is out! This release ships with exciting new features, most notably
with HTTP2 support, improved deve...
New
What learn first? Rust or Elixir
Hi Elixir community!
I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
Hi!
In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir?
Searched the docs for ip address and the web, no good results.
Thanks!
New
Hello everyone,
Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
What is the proper way to load a module from a file in to IEX?
In the python world, doing something like this pretty standard:
from ....
New
Update:
How to use the Blogs & Podcasts section
You can post links to your blog posts or podcasts either in one of the Official Blog...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








