umbra
Dealing with associations with Ecto
So once again Im stuck, I think that I have some problem with “database frameworks”, Im struggling really bad with Ecto, mainly with associations. Maybe the main issue here is my database design, who may be bad, even if this is the case, I need to be able to deal with it anyways.
My business logic is the following: I have a User(provided by Pow) which have many Expenses and Tags, and the Expense has many Tag. (Every User have his own Tags).
Im tryig to create a new Expense, I need to assoc a list of Tags and a User to it.
Params: %{“notes” => “I am a note!”, “tag_id” => [“2”, “3”], “value” => “12345”}
I have no clue on how to do it.
Pastebin links to the code:
Migrations
Schemas
Context and Assocs
Controller and Form
Thank you
Most Liked
stefanchrobot
I think Ecto should allow you to insert everything in one go and do some work for you. But if things get to complex or magical, remember that Repo.transaction is your friend and you can always do things more manually. Something like:
Repo.transaction(fn ->
with {:ok, schema1} <- Schema1.create_changeset(params1) |> Repo.insert(),
{:ok, schema2} <- Schema2.create_changeset(schema1, params2) |> Repo.insert(),
# ...
# Transaction returns {:ok, schemaN}
schemaN
else
# Transaction returns {:error, changeset}
{:error, changeset} -> Repo.rollback(changeset)
end
end)
or if you want to make things composable, you can use Multi:
Mulit.new()
|> Multi.insert(:schema1, Schema1.create_changeset(params1))
|> Multi.run(:schema2, fn repo, %{schema1: schema1} ->
schema1
|> Schema2.create_changeset(params2)
|> repo.insert()
end)
# ...
|> Repo.transaction()
|> case do
{:ok, %{schema1: schema1, ...}} -> {:ok, schemaN}
{:error, _failed_operation, failed_value, _changes_so_far} -> {:error, failed_value}
end
At this level things translate into straightforward SQL inserts/updates. If you go this way, I’d use pattern matching to destructure the params in the controller:
def create(conn, %{"schema1" => %{"attr1" => ..., "schema2" => %{ ... } = params2} = params1}) do
case Context.create_thing(params1, params2) do
# ...
end
end
The reason to do so would be to support params with both string keys (this is how data is coming in via Phoenix) and atom keys (more convenient to use in tests or IEx).
cmo
I don’t think there would be a user_id in that table as you have user_id on the tags table.
User has user_id, Tags are associated with a user_id (not associated with an expense so no expense_id column), Expenses have multiple tags, so you need an expense_tags table which is just expense_id and tag_id, no?
Popular in Questions
Other popular topics
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








