Hey there, this is my first post in here! I’m “just” in need of a little help with changesets and associations.
I have a changeset I built that works wonders to create and validate structs. However, now that I want to also add functionality to update structs this one’s not gonna do it since I’m using put_assoc and passing in the params to my helper function:
def changeset(%Course{} = struct, params \\ %{}) do
struct
|> cast(params, [:title, :description])
|> put_assoc(:scopes, parse_scopes(params), required: true)
|> validate_required([:title, :description])
end
defp parse_scopes(params) do
(params["scopes"] || [])
|> Enum.map(fn name ->
Repo.get_by(Scope, name: name)
end)
end
If I wanted to just update the title, I would just pass in the title through params and the existing course through struct, but that would mean the scopes would be reset to none since put_assoc destroys any association.
What is the best way to approach this problem?
Also, I’ve got another problem with this and it is that if the user passes in an invalid scope this will throw an error and I have no idea how to catch that in a clean way. I may open a new question for this later but I’d appreciate it if someone had some insight on this too.
If the record exists already and you wish to update it why not query for the parent and use Repo.preload to preload the scopes onto it.
def update_changeset(course_id,params) do
Repo.get(Course, course_id) |> Repo.preload([:scopes])
|> cast(params, [:title, :description])
|> cast_assoc(:scopes, required: true)
|> ....
end
If you preload the children Ecto should be able to figure out that you are updating rarther than creating a new one, as long as the params have ids for the scopes in them (which they should)
I don’t think I can use cast_assoc because I’m just passing in a list of strings from which i get the proper records and put them with put_assoc. From what I know, cast_assoc only works with a certain format for the params key.
I think I forgot to mention that I don’t pass the scopes struct, I just pass a string list through params. That may help a bit. I’m also not using Phoenix.