I have a table applications
with a foreign key user_id
that's a Postgres uuid
. I have in my web.ex
:
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
My model is:
defmodule Dashboard.Application do
use Dashboard.Web, :model
alias Dashboard.User
alias Dashboard.Path
schema "applications" do
field :name, :string
belongs_to :user, User
has_many :paths, Path
timestamps
end
@required_fields ~w(name user_id)
@optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
end
But when I try to do an insert with a changeset using a valid uuid from my users
table, I'm getting
[error] #PID<0.407.0> running Dashboard.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /applications
** (exit) an exception was raised:
** (Ecto.ChangeError) value `<<184, 235, 134, 244, 95, 86, 74, 133, 159, 153, 31, 111, 16, 28, 76, 15>>` for `Dashboard.Application.user_id` in `insert` does not match type :binary_id
(ecto) lib/ecto/query/planner.ex:33: anonymous fn/6 in Ecto.Query.Planner.fields/4
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/query/planner.ex:21: Ecto.Query.Planner.fields/4
(ecto) lib/ecto/repo/schema.ex:449: Ecto.Repo.Schema.dump_changes/5
(ecto) lib/ecto/repo/schema.ex:77: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
(ecto) lib/ecto/repo/schema.ex:477: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/9
(ecto) lib/ecto/pool.ex:292: Ecto.Pool.with_rollback/3
(ecto) lib/ecto/adapters/sql.ex:582: Ecto.Adapters.SQL.transaction/8
(ecto) lib/ecto/pool.ex:244: Ecto.Pool.outer_transaction/6
(ecto) lib/ecto/adapters/sql.ex:551: Ecto.Adapters.SQL.transaction/3
(dashboard) web/controllers/application_controller.ex:16: Dashboard.ApplicationController.create/2
Inspecting the user_id
I see:
pry(1)> params["user_id"] |> i
Term
<<184, 235, 134, 244, 95, 86, 74, 133, 159, 153, 31, 111, 16, 28, 76, 15>>
Data type
BitString
Byte size
16
Description
This is a binary: a collection of bytes. It's printed with the `<<>>`
syntax (as opposed to double quotes) because it is not a
UTF-8 encoded binary (the first invalid byte being `<<184>>`)
Reference modules
:binary
This looks like it's trying to insert a valid 16 byte uuid to me. What am I missing? Thanks!
UPDATE: Here's is the db schema:
Table "public.applications"
Column | Type | Modifiers
-------------+-----------------------------+-----------
id | uuid | not null
user_id | uuid | not null
name | text | not null
inserted_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
Indexes:
"applications_pkey" PRIMARY KEY, btree (id)
"applications_user_id_index" btree (user_id)
Foreign-key constraints:
"applications_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
Referenced by:
TABLE "paths" CONSTRAINT "paths_application_id_fkey" FOREIGN KEY (application_id) REFERENCES applications(id) ON DELETE CASCADE
Table "public.users"
Column | Type | Modifiers
-------------+-----------------------------+-----------
id | uuid | not null
email | text | not null
inserted_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
avatar | text | not null
name | text | not null
data | jsonb | not null
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_index" UNIQUE, btree (email)
Referenced by:
TABLE "applications" CONSTRAINT "applications_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
Update 2: I've upgraded to phoenix 1.2 and ecto 2.0.2 and the problem persists
Update 3: I think this may be a bug in Ecto. I've opened a PR with my best attempt at a fix: https://github.com/elixir-ecto/ecto/pull/1585
Ecto Schemas using binary_id
expects the data to be a String formatted UUID, which is then converted to the 16 byte binary format by Ecto automatically.
If you want to work with the binary representation directly, you can define the type as :binary_id
schema "applications" do
field :name, :string
belongs_to :user, User, foreign_key: :user_id, type: :binary_id
has_many :paths, Path
timestamps
end
Alternatively convert the binary data to a String with Ecto.UUID.load
:
iex(38)> binary = Ecto.UUID.bingenerate()
<<91, 154, 58, 233, 38, 235, 76, 200, 188, 162, 112, 23, 233, 223, 191, 144>>
iex(39)> Ecto.UUID.load(binary)
{:ok, "5b9a3ae9-26eb-4cc8-bca2-7017e9dfbf90"}
Check your migration file, and make sure you are defining the right data type for the id field.
add :user_id, references(:users, type: :uuid)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With