Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type mismatch inserting :binary_id with Ecto changeset

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

like image 804
Brad Urani Avatar asked Jul 18 '16 01:07

Brad Urani


2 Answers

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"} 
like image 196
Mike Buhot Avatar answered Nov 08 '22 19:11

Mike Buhot


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)

like image 20
Well Ronfim Avatar answered Nov 08 '22 18:11

Well Ronfim