marcin
Hi! ![]()
I’d like to ask how you implement a super common functionality, for which I do not see any easy way in Ecto.Changeset
Lets say I have a Todo which belongs to a TodoList.
I get my auto-generated changeset() using phx.gen.schema which looks roughly like this:
def changeset(todo, attrs) do
todo
|> cast(attrs, [:title, :details, :done])
|> validate_required([:title])
end
Now I would like to also be able to pass the association into this function, so in my context API todos.ex I can do:
def move_todo_to_list(todo, todo_list) do
update_todo(todo, %{todo_list: todo_list})
end
obviouly in changeset() I cannot cast() the todo_list.
I need to call either put_assoc(), or change() - however, the association is not always given, I can also just have this usage from controller
def create_todo(params) do
%Todo{}
|> Todo.changeset(params)
|> Repo.insert()
end
where params would contain todo_list_id…
I do not want to do any conditional handling in my changeset, nor defp some multiclause helpers:
def changeset(todo, attrs) do
chset = todo
|> cast(attrs, [:title, :details, :done])
# OMG verbose!!!!
chset = if attrs[:todo_list] do
put_assoc(chset, attrs[:todo_list], todo_list)
else
chset
end
chset
|> validate_required([:title])
end
(note, some custom validations might use the association to figure out if an attribute is valid or not, so validate goes last).
I used to do kinda elegant:
def changeset(todo, attrs) do
assocs = Map.take(attrs, [:todo_list])
todo
|> cast(attrs, [:title, :details, :done])
|> change(assocs)
|> validate_required([:title])
end
But this will not work if attrs are sometimes keyed by strings (data from user), and sometimes by atoms (programmer controlled params from some module), and then when I add :todo_list key, i might end up with “cannot mix strings and atom keys” error from cast().
I bet this has to be solved somehow elegantly, this is such a common use case – but cannot come up with anything based on Ecto standard functions other then writing some custom helpers…
Trending in Questions
Other Trending 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
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #phoenix_html
- #elixirconf-us
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
LostKobrakai
How about the following?
marcin
That does not cover a useful situation when the assoc is not yet in the db (let’s say I created a new todo with its own list, and will let Repo.insert both)
LostKobrakai
Tbh at a certain point it’s just not worth it trying to treat every possible scenario as “one and the same”. Ecto has useful APIs for all the usecases you mentioned. It’s however not going to be one API covering all of them at the same time.
dimitarvp
I agree with @LostKobrakai here but if you really insist, you can just make a function with multiple heads, one of which covers a
nilprimary / foreign key.