elixir-ecto / ecto

A toolkit for data mapping and language integrated query.
https://hexdocs.pm/ecto
Apache License 2.0
6.13k stars 1.43k forks source link

Preload generates wrong query #3363

Closed thojanssens closed 4 years ago

thojanssens commented 4 years ago

See update below. :source is not the problem.


Say I have those two schemas:

defmodule EctoTest.Item do
  use Ecto.Schema

  schema "items" do
    belongs_to :group, EctoTest.ItemGroup, source: :item_group_id
    timestamps(type: :utc_datetime)
  end
end

defmodule EctoTest.ItemGroup do
  use Ecto.Schema

  schema "items_groups" do
    has_many :items, EctoTest.Item
    timestamps(type: :utc_datetime)
  end
end

It seems there's no way to execute a query like below, where I use the :group field (which has a different source name in DB):

Repo.all from i in Item, preload: [group: :items]

This will raise the error

** (Ecto.QueryError) deps/ecto/lib/ecto/association.ex:670: field item_group_id in where does not exist in schema EctoTest.Item in query:

from i0 in EctoTest.Item, where: i0.item_group_id in ^[1, 4, 5, 6], order_by: [asc: i0.item_group_id], select: {i0.item_group_id, i0}

thojanssens commented 4 years ago

I did further testing and :source seems not to be the problem. I removed source and changed the schemas to:

defmodule EctoTest.Item do
  use Ecto.Schema

  schema "items" do
    belongs_to :group, EctoTest.ItemGroup
    timestamps(type: :utc_datetime)
  end
end

defmodule EctoTest.ItemGroup do
  use Ecto.Schema

  schema "items_groups" do
    has_many :items, EctoTest.Item
    timestamps(type: :utc_datetime)
  end
end

In the migration for "items" I have:

add :group_id, references(:items_groups), null: true

The following query will still raise an error:

Repo.all from i in Item, preload: [group: :items]

** (Ecto.QueryError) deps/ecto/lib/ecto/association.ex:664: field item_group_id in where does not exist in schema EctoTest.Item in query:

from i0 in EctoTest.Item, where: i0.item_group_id == ^1, order_by: [asc: i0.item_group_id], select: {i0.item_group_id, i0}

(elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3 (elixir 1.10.2) lib/enum.ex:1520: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3

josevalim commented 4 years ago

@thojanssens the association is inferring the references to be given by the column item_group_id and not group_id. If you check has_many docs, it says:

  * :foreign_key - Sets the foreign key, this should map to a field on the other schema, defaults to the underscored name of the current schema suffixed by _id

Taht's why it tries to use item_group_id. Pass this option explicitly and you should be good.