How to update a foreign key set to nil

The code below all works except for the very last line.

Summary:

I have 2 entries from two different tables.
I query to see if they both have the same value assigned to their respective “name” keys. In this case they do - name:“Zarnog”.
I then take the id of StatusActions and attempt to set it to TestBed’s status_id key.
The result is that TestBed’s status_id remains set to nil. No error is thrown.

I want to take StatusAction’s id and set it to TestBed’s status_id field. How can I do this?

 selected_sa = StatusActions.get_by(%{testbed_name: "Zarnog"})
      IO.inspect selected_sa.id
       selected_testbed = TestBeds.get_by(%{name: "Zarnog"})
      IO.inspect selected_testbed   
      TestBeds.update_test_bed(selected_testbed, %{status_id: selected_sa.id})

Hello, me guess is that probably the TestBeds’ changeset used by update is not casting :status_id. Hth

It would be helpful to see the implementation of TestBeds.update_test_bed. But in cases where the value is generated by the system and is not user input that needs to be validated, I would set it directly:

selected_testbed
|> Ecto.Changeset.change(status_id: selected_sa.id)
|> Repo.update!()
1 Like
selected_testbed
|> Ecto.Changeset.change(status_id: selected_sa.id)
|> Repo.update!()

I didn’t even know those modules were available without importing them.

I also don’t understand why your code works to change the ID and mine does not.

I wrote out some of the code in case you’re bored and want to comment. I created TestBed in the terminal when I created the phoenix instance.

mix phx.gen.context StatusActions StatusAction  status_actions testbed_name:string   username:string  status:string  reason:string  available:boolean 

mix phx.gen.context TestBeds TestBed  test_beds  name:string  version:string note:string    status_id:references:status_actions

I am using the built in methods that the app gave me except one.

I created

  def get_by(data)do  
     Repo.get_by(TestBed, data)
  end

Update is created for me:

  def update_test_bed(%TestBed{} = test_bed, attrs) do
    test_bed
    |> TestBed.changeset(attrs)
    |> Repo.update()
  end


defmodule App.TestBeds.TestBed do
  use Ecto.Schema
  import Ecto.Changeset

  schema "test_beds" do
    field :name, :string
    field :note, :string
    field :version, :string
    field :status_id, :id

    timestamps()
  end

  @doc false
  def changeset(test_bed, attrs) do
    test_bed
    |> cast(attrs, [:name, :version, :note])
    |> validate_required([:name, :version, :note])
  end
end

import and alias are just shortcuts for referencing modules. You never need to import functions (macros are another story).

Your changeset functions are all inside App.TestBeds.TestBed, which was scaffolded for you. If you use that, you will need to update that function to support any attributes/associations, or any other kind of changes you want to make. As @patrickdm guessed, you are not casting the attribute you want to change. But you don’t have to use that function, it’s just a conventional helper generated by the scaffolding to get you started. Another pattern (which I prefer) is just to build your changesets right inside your context function:

  def update_test_bed(%TestBed{} = test_bed, attrs) do
    test_bed
    |> TestBed.changeset(attrs)
    |> cast(attrs, [:name, :version, :note, :status_id]) # note the new field added here
    |> validate_required([:name, :version, :note])
    |> Repo.update()
  end

More information here: Changesets · Elixir School

1 Like