Repo.update foreign key not working

I’m trying to learn myself ecto. I have a people table which can be part of a people_group. Now I want to update a people record and update the people_group_id, but the code below does not update it. What am I missing?

iex(26)> person = Friends.Repo.get!(Friends.Person,2) |> Friends.Repo.preload(:people_group)
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27,
 bio: "abcdefghijklmnopqr", first_name: "John", id: 2, last_name: "Smith",
 people_group: nil, people_group_id: nil}
iex(27)> 
13:46:05.915 [debug] QUERY OK source="people" db=2.4ms queue=0.1ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age", p0."bio", p0."people_group_id" 
FROM "people" AS p0 WHERE (p0."id" = $1) [2]                                      
iex(27)> changeset = Friends.Person.changeset(person, %{"people_group_id" => 1})            
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #Friends.Person<>,
 valid?: true>
iex(28)> Friends.Repo.update(changeset)                                                     
{:ok,
 %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27,
  bio: "abcdefghijklmnopqr", first_name: "John", id: 2, last_name: "Smith",
  people_group: nil, people_group_id: nil}}

For completeness

defmodule Friends.PersonGroup do
  use Ecto.Schema

  schema "people_group" do
    field :name, :string
    has_many :people, Friends.Person
  end

  def changeset(person_group, params \\ %{}) do
    person_group
    |> Ecto.Changeset.cast(params, [:name,])
    |> Ecto.Changeset.validate_required([:name])
    |> Ecto.Changeset.validate_length(:name, min: 3)
  end
end
defmodule Friends.Person do
  use Ecto.Schema

  schema "people" do
    field :first_name, :string
    field :last_name, :string
    field :age, :integer
    field :bio, :string
    belongs_to :people_group, Friends.PersonGroup
  end

  def changeset(person, params \\ %{}) do
    person
    |> Ecto.Changeset.cast(params, [:first_name, :last_name, :age, :bio, :people_group_id])
    |> Ecto.Changeset.validate_required([:first_name, :last_name, :bio])
    |> Ecto.Changeset.validate_length(:bio, min: 15)
    |> Ecto.Changeset.assoc_constraint(:people_group)
  end
end
1 Like

It should be writing it fine, are you not seeing it properly in the database?

Remember that the returned record updates the ID if any change to it, the other fields are not updated unless read_after_write or whatever it was called ( ^.^; ) is set on the field or you query it yourself. :slight_smile:

2 Likes

Thanks. I just found the problem. Updated elixir from 1.4.0 to 1.4.2 and it works. The preload appears not necessary.
Btw: In 1.4.0 the db was for sure not updated, I had a look in pgadmin.

iex(9)> person = Friends.Repo.get!(Friends.Person, 4)                                       

15:37:55.558 [debug] QUERY OK source="people" db=1.4ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age", p0."bio", p0."people_group_id" FROM "people" AS p0 WHERE (p0."id" = $1) [4]
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 8,
 bio: "aaa234567890123456", first_name: "Klara", id: 4, last_name: "Koe",
 people_group: #Ecto.Association.NotLoaded<association :people_group is not loaded>,
 people_group_id: nil}
iex(10)> changeset = Friends.Person.changeset(person, %{"people_group_id" => 1}) 
#Ecto.Changeset<action: nil, changes: %{people_group_id: 1}, errors: [],
 data: #Friends.Person<>, valid?: true>
iex(11)> Friends.Repo.update(changeset)                                         

15:38:10.283 [debug] QUERY OK db=0.4ms queue=0.1ms
begin []

15:38:10.285 [debug] QUERY OK db=1.5ms
UPDATE "people" SET "people_group_id" = $1 WHERE "id" = $2 [1, 4]

15:38:10.287 [debug] QUERY OK db=1.5ms
commit []
{:ok,
 %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 8,
  bio: "aaa234567890123456", first_name: "Klara", id: 4, last_name: "Koe",
  people_group: #Ecto.Association.NotLoaded<association :people_group is not loaded>,
  people_group_id: 1}}
iex(12)> 
1 Like