Ecto: cast_assoc and server generated UUIDs

Consider a schema with the following settings for the primary key:

@primary_key {:id, :binary_id, autogenerate: false}

In this case, we’re using server generated UUIDs in some cases. For a regular insert, this is fine. I just pass in returning: [:id]. However, this is problematic for cast_assoc/3. The related models get created in the database but Ecto doesn’t set the keys on the parent model.

Here is a contrived example:

defmodule NS.User do
  use Ecto.Schema

  @primary_key {:id, :binary_id, autogenerate: false}

  @foreign_key_type :binary_id

  schema "users" do
    belongs_to :profile, NS.Profile, on_replace: :update
  end
end

defmodule NS.Profile do
  use Ecto.Schema

  @primary_key {:id, :binary_id, autogenerate: false}

  @foreign_key_type :binary_id

  schema "user_profiles" do
    field :name, :string
  end
end

attrs = %{profile: %{name: "Name"}}

user
|> cast_assoc(:profile, attrs)
|> NS.Repo.insert(returning: [:id])

The NS.Profile does get created, but it’s not associated with NS.User (:profile_id is nil, even after a reload). Setting autogenerate to true fixes this issue. However, I imagine for integer keys the value coming back from the server is being used and is not generated by the application.

For now, autogenerating UUIDs for some models is fine, but there are other models where this is being done server-side and we want that functionality. What can I do to make those situations work with cast_assoc/3?

Using Ecto 3.8.4.

@primary_key false

schema … do
  field :id, :binary_id, primary_key: true, read_after_writes: true
end

This should do. Not sure if you can pass read_after_writes: true on @primary_key

1 Like

That did the trick.

Also, I played around a bit too and you can indeed use :read_after_writes: true on @primary_key. If you include autogenerate: true on a :binary_id it’ll even give you an error.

I don’t know why I didn’t think to take a deeper look at the schema docs! I was looking through changesets and repos. I’ll review those docs now.

Thanks a bunch.