Updating single row table

I have a table that I want to constrain to a single row…

def up do
    create table(:config, primary_key: false) do
      add :id, :integer, primary_key: true, null: false
  ...more fields
    create constraint("config", :only_one_record, check: "id = 1")
    execute "INSERT INTO " <> prefix() <> ".config (id, ...more fields) VALUES(1, ...);"

I can get the record:

 Repo.get!(Config, 1,

but can’t figure out how to update

    |> Config.update_changeset(attrs)
    |> Repo.update(

** (Ecto.NoPrimaryKeyValueError) struct %Config{__meta__: #Ecto.Schema.Metadata<:built, "config">, id: nil, more fields} is missing primary key value

I tried setting the id explicitly in the changeset, but get the same error.

What is the value of config before passing it to Config.update_changeset/2?

 def update_config(%Config{} = config, attrs, tenant) do
    |> Config.update_changeset(attrs)
    |> IO.inspect()
    |> Repo.update(prefix: Analytics.Tenants.Actions.build_prefix(tenant))

action: nil,
changes: %{id: 1, screen_rec_storage: true, voice_rec_storage: true},
errors: [],
data: #Analytics.Tenants.Config<>,
valid?: true

  1. test config settings Update config (Analytics.Tenants.TenantTest)
    ** (Ecto.NoPrimaryKeyValueError) struct %Analytics.Tenants.Config{__meta__: #Ecto.Schema.Metadata<:built, "config">, id: nil, screen_rec_storage: nil, voice_rec_storage: nil} is missing primary key value
    code: assert {:ok, _} = Tenants.update_config(%Config{}, cfg, tenant)
    (ecto 3.6.2) lib/ecto/repo/schema.ex:932: anonymous fn/3 in Ecto.Repo.Schema.add_pk_filter!/2
    (elixir 1.12.0) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ecto 3.6.2) lib/ecto/repo/schema.ex:397: Ecto.Repo.Schema.do_update/4
    test/analytics/tenants_test.exs:21: (test)

Empty config is passed. Of course, id is absent

ah, oh
Well, I guess I don’t fully understand the whole Ecto thing yet.
Thanks for the help

Yes, the schema update function executed in the stracktrace at lib/ecto/repo/schema.ex:397 calls add_pk_filter!(changeset.filters, struct) with the struct that’s in your changeset’s :data, which in this case is empty of values.

You have to pass in a %Config{id: 1} struct to update_config

That is, in your code do:

assert {:ok, _} = Tenants.update_config(%Config{id: 1}, cfg, tenant)

That ends up telling Repo that you want to update the row with id of 1, if you don’t pass that in as your base data, it won’t know what to update.

You can get away with an empty %Config{} struct when inserting because it is a new entry.

Thanks for taking the time to explain

1 Like