How could I access the changes from this?

Title


%Ecto.Multi{
  names: #MapSet<["profile"]>,
  operations: [
    {"profile",
     {:changeset,
      #Ecto.Changeset<
        action: :update,
        changes: %{first_name: "bence32", last_name: "cccdsadsaasc"},
        errors: [],
        data: #XD.Accounts.Profile<>,
        valid?: true
      >, []}}
  ]
}

For deep nested struct, I would reach for get_in and/or put_in.

yeah get_in seems good, but I cant seems to get it work yet, it says nil for this

get_in(List.first(invited_user.operations), :changeset)
get_in(List.first(invited_user.operations), :changes)

Per the documentation, you shouldn’t access the Multi internals directly. Instead, you should call to_list/1. Then you can filter the result based on the tag of the operation you’re interested in (:profile in your case). You can see a (limited) example in the module docs.

So probably something like this:

  %Ecto.Multi{
    names: #MapSet<["profile"]>,
    operations: [
      {"profile",
       {:changeset,
        #Ecto.Changeset<
          action: :update,
          changes: %{first_name: "bence32", last_name: "cccdsadsaasc"},
          errors: [],
          data: #XD.Accounts.Profile<>,
          valid?: true
        >, []}}
    ]
  }
  |> Ecto.Multi.to_list()
  |> Keyword.fetch!(:profile)
  |> elem(1)
  |> Keyword.get(:changes)
3 Likes

Not tested… You could always pattern match…

%{
  operations: [{"profile", {:changeset, %{changes: changes}, []}}|_rest]
}

PS: You should add 3 ` at the beginning and the end of your code, and not put this into quotes… It’s not easy to read when it’s not formatted :slight_smile:

it looks good, but it gives argument error on this line:

What’s the output if you add IO.inspect() before:

...
|> IO.inspect()
|> elem(1)
...

just checked,
:error

Look like you’re using fetch/2 instead of fetch!/2 and that your name is a string instead of an atom. This should work:

%Ecto.Multi{
    names: #MapSet<["profile"]>,
    operations: [
      {"profile",
       {:changeset,
        #Ecto.Changeset<
          action: :update,
          changes: %{first_name: "bence32", last_name: "cccdsadsaasc"},
          errors: [],
          data: #XD.Accounts.Profile<>,
          valid?: true
        >, []}}
    ]
  }
  |> Ecto.Multi.to_list()
  |> Keyword.fetch!("profile")
  |> elem(1)
  |> Keyword.get(:changes)

yeah, I tried this with the string, but this gives a new error:

no function clause matching in Keyword.fetch!/2

Can you show what the output of Ecto.Multi.to_list(multi) is?

Can you please expand why you need to access the changes field inside a multi?

2 Likes
[
  {"profile",
   {:update,
    #Ecto.Changeset<
      action: :update,
      changes: %{first_name: "bence32", last_name: "cccdsadsaasc"},
      errors: [],
      data: #XD.Accounts.Profile<>,
      valid?: true
    >, []}}
]

I need a user inside the multi with the new values

Oops, by using a string as a key the result of to_list/1 is no longer a keyword list… So you need this instead:

%Ecto.Multi{
    names: #MapSet<["profile"]>,
    operations: [
      {"profile",
       {:changeset,
        #Ecto.Changeset<
          action: :update,
          changes: %{first_name: "bence32", last_name: "cccdsadsaasc"},
          errors: [],
          data: #XD.Accounts.Profile<>,
          valid?: true
        >, []}}
    ]
  }
  |> Ecto.Multi.to_list()
  |> Enum.find(fn
    {"profile", profile} ->profile
    _ -> false
  end)
  |> elem(1)
  |> Keyword.get(:changes)

Can you set/access this information before you put it inside the multi?

2 Likes

That’s not how you would get that. A Ecto.Multi struct is nothing but some callbacks before it’s passed to Repo.transaction. If you need values from prev. steps in a multi use Ecto.Multi.run or the other functions, where you can pass callbacks as arguments.

1 Like

ok so it seems like this approach is not good, even though i can access it from earlier, i dont have id etc, so i will need to do he Multi.run approach

so if i do the update in Multi.run then how do i get the user with the updated values?

  Multi.run(mult, "profile", fn _repo, x ->
                Repo.update(x)end)
 Accounts.get_user_by_phone_number(p["phone_number"])


because this returns the old one

The argument passed into the run call contains a map of the results up to that point by their IDs.

1 Like