Hi!
I would like to know how do you handle the following cases.
In this example a manager can be set with a project, and when a manager’s project change, we check if the project is started and in that case, we force the manager to be “on site”. nil
can also be set to remove the manager from any project.
Now the changeset attributes can come from different sources (http api or internal) so as many other applications we allow binary or atom attributes.
This gives us the following code.
# Context Functions
def update_manager(%Manager{} = manager, attrs) do
manager =
case attrs do
%{project: _} -> Repo.preload(manager, :project)
%{"project" => _} -> Repo.preload(manager, :project)
%{} -> manager
end
manager
|> Manager.changeset(attrs)
|> Repo.update()
end
# Manager Schema
belongs_to :project, MyApp.Project
# Manager Functions
def changeset(manager, attrs) do
manager
|> cast(attrs, [:name, :on_site])
|> validate_required([:name])
|> handle_assoc(:project, attrs)
end
defp handle_assoc(cset, :project, %{project: project}), do: set_project(cset, project)
defp handle_assoc(cset, :project, %{"project" => project}), do: set_project(cset, project)
defp handle_assoc(cset, :project, _), do: cset
defp set_project(cset, project) do
case project do
nil ->
put_assoc(cset, :project, nil)
%Project{started: started?} ->
cset = put_assoc(cset, :project, project)
if started?,
do: put_change(cset, :on_site, true),
else: cset
end
end
I am not satisfied with that. We need to check atom and binary to preload (as we cannot preload during changeset operations – I found a topic about that but it’s still considered as a hack), and then we need to check both keys again so put the association.
How would you do this in a clean way?