elixir-lang/ecto — pull request #222 by TanYewWei (2014-05-13 11:49:47+00:00)
Title: Always put_primary_key after Repo.insert to ensure associations are set properly

Related to the previous pull request 220 -- https://github.com/elixir-lang/ecto/pull/220

An issue popped up after update to the latest master (https://github.com/elixir-lang/ecto/tree/fdcf807901683c73c1117d89751e0d5a7a318154)

The problem is with `has_one` and `has_many` associations not being properly set after a a call to `Repo.insert`. `belongs_to` associations seem to be fine though.

A minimum failing test can be achieved by adding the following:

* in `integration_test/pg/test_helper.exs` line 94

```elixir
defmodule Ecto.Integration.Postgres.AssignedPrimaryKey do
use Ecto.Model
@schema_defaults [primary_key: {:id, :string, []}]

schema "assigned_primary_keys" do
has_one :user, Ecto.Integration.Postgres.User
end
end
```

* in `integration_test/pg/repo_test.exs` line 103

```elixir
test "create with user-assigned primary key" do
model = TestRepo.insert(%AssignedPrimaryKey{id: "id"})
assert model.id == "id"
assert model == TestRepo.get(AssignedPrimaryKey, "id")
assert_raise Postgrex.Error, fn -> TestRepo.insert(%AssignedPrimaryKey{}) end
end
```

Running postgres integration tests leads to the following error:

```elixir
1) test create with user-assigned primary key (Ecto.Integration.RepoTest)
Assertion with == failed
code: model == TestRepo.get(AssignedPrimaryKey, "id")
lhs: %Ecto.Integration.Postgres.AssignedPrimaryKey{id: "id",
user: {Ecto.Associations.HasOne.Proxy,
#Ecto.Associations.HasOne<[name: :user, target: Ecto.Integration.Postgres.AssignedPrimaryKey,
associated: Ecto.Integration.Postgres.User, references: :id, foreign_key: :assignedprimarykey_id]>}}
rhs: %Ecto.Integration.Postgres.AssignedPrimaryKey{id: "id",
user: {Ecto.Associations.HasOne.Proxy,
#Ecto.Associations.HasOne<[name: :user, target: Ecto.Integration.Postgres.AssignedPrimaryKey,
associated: Ecto.Integration.Postgres.User, references: :id, foreign_key: :assignedprimarykey_id]>}}
integration_test/pg/repo_test.exs:105
...................................................................

Finished in 5.0 seconds (2.2s on load, 2.8s on tests)
84 tests, 1 failures
```

Modifying the `Ecto.Repo.Backend.insert/4` function to be the following fixes the issue:

```elixir
def insert(repo, adapter, model, opts) do
normalized_model = normalize_model(model)
validate_model(normalized_model)

result = adapter.insert(repo, normalized_model, opts)
module = model.__struct__
pk_field = module.__schema__(:primary_key)

if pk_field do
pk_value = Dict.get(result, pk_field) ||
Map.get(normalized_model, pk_field)
model = Ecto.Model.put_primary_key(model, pk_value)
end

struct(model, result)
end
```