Ecto: build_assoc don't setup the related id

Created on 10 Mar 2017  路  4Comments  路  Source: elixir-ecto/ecto

Environment

Elixir version: Elixir 1.4.2
Database: Postgres 9.6.1-2.pgdg80+1
Ecto version (mix deps):

* idna 4.0.0 (Hex package) (rebar3)
  locked at 4.0.0 (idna) 10aaa9f7
  ok
* inflex 1.8.0 (Hex package) (mix)
  locked at 1.8.0 (inflex) 7cfc752a
  ok
* mimerl 1.0.2 (Hex package) (rebar3)
  locked at 1.0.2 (mimerl) 993f9b0e
  ok
* connection 1.0.4 (Hex package) (mix)
  locked at 1.0.4 (connection) a1cae722
  ok
* metrics 1.0.1 (Hex package) (rebar3)
  locked at 1.0.1 (metrics) 25f094de
  ok
* fs 0.9.1 (Hex package) (rebar)
  locked at 0.9.2 (fs) ed17036c
  ok
* gettext 0.13.1 (Hex package) (mix)
  locked at 0.13.1 (gettext) 5e0daf4e
  ok
* ranch 1.3.2 (Hex package) (rebar3)
  locked at 1.3.2 (ranch) e4965a14
  ok
* poolboy 1.5.1 (Hex package) (rebar)
  locked at 1.5.1 (poolboy) 6b461639
  ok
* decimal 1.3.1 (Hex package) (mix)
  locked at 1.3.1 (decimal) 157b3ced
  ok
* poison 3.1.0 (Hex package) (mix)
  locked at 3.1.0 (poison) d9eb6366
  ok
* ssl_verify_fun 1.1.1 (Hex package) (rebar)
  locked at 1.1.1 (ssl_verify_fun) 28a4d65b
  ok
* scrivener 2.2.1 (Hex package) (mix)
  locked at 2.2.1 (scrivener) 5a84cdfc
  ok
* combine 0.9.6 (Hex package) (mix)
  locked at 0.9.6 (combine) 8d1034a1
  ok
* certifi 1.0.0 (Hex package) (rebar3)
  locked at 1.0.0 (certifi) 1c787a85
  ok
* hackney 1.7.1 (Hex package) (rebar3)
  locked at 1.7.1 (hackney) e238c52c
  ok
* tzdata 0.5.10 (Hex package) (mix)
  locked at 0.5.10 (tzdata) 087e8dfe
  ok
* timex 3.1.13 (Hex package) (mix)
  locked at 3.1.13 (timex) 48b33162
  ok
* db_connection 1.1.1 (Hex package) (mix)
  locked at 1.1.1 (db_connection) f9d246e8
  ok
* httpoison 0.11.1 (Hex package) (mix)
  locked at 0.11.1 (httpoison) d06c5712
  ok
* phoenix_pubsub 1.0.1 (Hex package) (mix)
  locked at 1.0.1 (phoenix_pubsub) c10ddf62
  ok
* oauth2 0.8.3 (Hex package) (mix)
  locked at 0.8.3 (oauth2) 080ea0bf
  ok
* cowlib 1.0.2 (Hex package) (rebar3)
  locked at 1.0.2 (cowlib) 9d769a1d
  ok
* cowboy 1.1.2 (Hex package) (rebar3)
  locked at 1.1.2 (cowboy) 61ac29ea
  ok
* mime 1.1.0 (Hex package) (mix)
  locked at 1.1.0 (mime) 01c1d6f4
  ok
* plug 1.3.0 (Hex package) (mix)
  locked at 1.3.0 (plug) 6e2b01af
  ok
* phoenix_html 2.9.3 (Hex package) (mix)
  locked at 2.9.3 (phoenix_html) 1b5a2122
  ok
* ja_serializer 0.12.0 (Hex package) (mix)
  locked at 0.12.0 (ja_serializer) ba4ec5fc
  ok
* ueberauth 0.4.0 (Hex package) (mix)
  locked at 0.4.0 (ueberauth) bc72d5e5
  ok
* ueberauth_facebook 0.6.0 (Hex package) (mix)
  locked at 0.6.0 (ueberauth_facebook) 51006ed2
  ok
* phoenix 1.2.1 (Hex package) (mix)
  locked at 1.2.1 (phoenix) 6dc59224
  ok
* phoenix_live_reload 1.0.8 (Hex package) (mix)
  locked at 1.0.8 (phoenix_live_reload) 4333f9c7
  ok
* postgrex 0.13.1 (Hex package) (mix)
  locked at 0.13.1 (postgrex) ebf17b73
  ok
* ecto 2.1.3 (Hex package) (mix)
  locked at 2.1.3 (ecto) ffb24e15
  ok
* timex_ecto 3.1.1 (Hex package) (mix)
  locked at 3.1.1 (timex_ecto) 37d54f68
  ok
* ecto_enum 1.0.1 (Hex package) (mix)
  locked at 1.0.1 (ecto_enum) 7853cd36
  ok
* scrivener_ecto 1.1.4 (Hex package) (mix)
  locked at 1.1.4 (scrivener_ecto) 056aa1f3
  ok
* straw_hat 0.0.1 (../straw_hat) (mix)
  ok
* phoenix_ecto 3.2.2 (Hex package) (mix)
  locked at 3.2.2 (phoenix_ecto) 2e51c576
  ok
  • Operating system: MacOS 10.12.3

Current behavior

Migrations

  create table(:users) do
  # ...
  end

  create table(:identities) do
    add :user_id, references(:users, on_delete: :delete_all), null: false
  end

Schemas

defmodule UserSchema do
   # ...

  schema "users" do
    has_many :identities, IdentitySchema, on_delete: :delete_all
  end
end

defmodule IdentitySchema do
  #...
  @required_fields ~w(user_id)a
  @optional_fields ~w()a

  schema "identities" do
    belongs_to :user, UserSchema
  end

  def changeset(model, params \\ %{}) do
    model
    |> cast(params, @required_fields ++ @optional_fields)
    |> validate_required(@required_fields)
    |> assoc_constraint(:user)
  end
end

Code

defp create_identity(user, auth) do
    identity = Ecto.build_assoc(user, :identities)

    IO.inspect(String.duplicate("*", 100))
    IO.inspect(user)
    IO.inspect(String.duplicate("-", 100))
    IO.inspect(identity)
    IO.inspect(String.duplicate("*", 100))

    params = get_params_from_auth(auth)
    changeset = IdentitySchema.changeset(identity, params)

    case AppRepo.insert(changeset) do
      {:error, reason} -> {:error, reason}
      _ -> {:ok, user}
    end
  end

Output

"****************************************************************************************************"
%UserSchema{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
 id: 1,
 identities: #Ecto.Association.NotLoaded<association :identities is not loaded>}
"----------------------------------------------------------------------------------------------------"
%{__meta__: #Ecto.Schema.Metadata<:built, "identities">,
  __struct__: IdentitySchema,
  id: nil,
  user: #Ecto.Association.NotLoaded<association :user is not loaded>,
  user_id: nil,
  user_schema_id: 1}
"****************************************************************************************************"

Reason of failing

{:error,
 #Ecto.Changeset<action: :insert,
  changes: %{},
  errors: [user_id: {"can't be blank", [validation: :required]}],
  data: #IdentitySchema<>, valid?: false>}

Expected behavior

The user_id should have the id from the user. For some reason it's under user_schema_id. Following the Ecto.build_assoc documentation.

iex> post = Repo.get(Post, 13)
%Post{id: 13}
iex> build_assoc(post, :comments)
%Comment{id: nil, post_id: 13}

The Comment have the post_id setup which is what I am expecting

Most helpful comment

Since ecto cannot rely on the association module accessible at compile-time (since that would create a cyclic dependency). Ecto tries to infer this on a bes-effort case using the related schema name as the base adding the _id suffix - you can provide the name explicitly with foreign_key option.

This is actually the same issue as https://github.com/elixir-ecto/ecto/issues/1948 (although in a different place), so I'm going to close this one and merge with the other.

All 4 comments

This keep happening to me in other cases and I ended up just adding it manually, I always notice that [module_name]_id is actually the one that holds the value for some reason

Since ecto cannot rely on the association module accessible at compile-time (since that would create a cyclic dependency). Ecto tries to infer this on a bes-effort case using the related schema name as the base adding the _id suffix - you can provide the name explicitly with foreign_key option.

This is actually the same issue as https://github.com/elixir-ecto/ecto/issues/1948 (although in a different place), so I'm going to close this one and merge with the other.

@michalmuskala aaaaahhh I understand what you are doing.

@michalmuskala would you mind to tell me where this line https://github.com/elixir-ecto/ecto/blob/master/lib/ecto.ex#L465 jump to please? I can't follow the code 馃槩

@yordis the build functions per association defined on this module: https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/association.ex

Was this page helpful?
0 / 5 - 0 ratings