Search code examples
modelelixirphoenix-frameworkecto

Getting actual data of model


Suppose I've some model already and after some time I'll changing some field of this model in another function.

Is there any nice way to get the actual data(state) of the model from DB without using standard 'MyRepo.get(MyModel, model.id)' ?

case MyModel.create(attr) do
  {:ok, model} ->
    ...some code...
    # here model will update
    Task.start(fn -> Model.do_some(model) end)
    # here I need model with actual data from DB
    {:reply, {:ok, ModelView.render("show.json", model: model)}, socket}
  {:error, message} ->
    ...some code...
end

P.s. the model can have some preloaded association


Solution

  • Instead of reloading your model, you should use the result of Model.do_some(model), which should return the updated model.

    Because even if you reload your model after Task.start, you can't be sure that Model.do_some(model) was already executed since it was started in a new task asynchronously.

    So either you do it in the same process as the caller:

    case MyModel.create(attr) do
      {:ok, model} ->
        model = Model.do_some(model)
        {:reply, {:ok, ModelView.render("show.json", model: model)}, socket}
      {:error, message} ->
    end
    

    or in a new task (But I'm not sure what the benefit would be)

    case MyModel.create(attr) do
      {:ok, model} ->
        task = Task.start(fn -> Model.do_some(model) end)
        case Task.yield(task) || Task.shutdown(task) do
          {:ok, model} ->
            {:reply, {:ok, ModelView.render("show.json", model: model)}, socket}
          _ -> # Timeout reached
        end  
      {:error, message} ->
    end
    

    But still, to answer your main question: No, there is no way of automatically reloading your model. However, you can define a helper method in your repo:

    def refresh(%module{id: id}) do
      get(module, id)
    end
    

    You will still have to preload associations manually.