составная уникальная ошибка ограничения при обновлении набора изменений - PullRequest
0 голосов
/ 11 июня 2019

У меня есть схема two_fa_details, где поля answer и question_id являются полями, и оба они уникальны вместе. Теперь, когда я пытаюсь вставить в него данные сначала, они вставляются, но в следующий раз их обновление не работает .. Это говорит об ограниченииошибка.

У меня есть функция set_two_factor_details, написанная для обновления таблицы. Функция отлично работает для вставки данных очень рано ... но когда я обновляю их ... она не работает ... у меня есть PUT APIдля этой функции.это мой файл миграции для схемы two_fa_details

def change do
    create table(:two_fa_details) do
      add :answer, :string
      add :userprofile_id, references(:user_profile, on_delete: :nothing)
      add :question_id, references(:questions, on_delete: :nothing)

      timestamps()
    end

    create index(:two_fa_details, [:userprofile_id])
    create index(:two_fa_details, [:question_id])

    create unique_index(:two_fa_details, [:userprofile_id, :question_id], name: :user_twofa_detail)
  end

здесь приведен фрагмент кода

def set_twofactor_details(client_id, twofa_records) do
    user = Repo.get_by(UserProfile, client_id: client_id)
    twofa_records = Enum.map(twofa_records, &get_twofa_record_map/1)

    Enum.map(twofa_records, fn twofa_record ->
      Ecto.build_assoc(user, :two_fa_details)
      |> TwoFaDetails.changeset(twofa_record)
    end)
    |> Enum.zip(0..Enum.count(twofa_records))
    |> Enum.reduce(Ecto.Multi.new(), fn {record, id}, acc ->
      Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record)
    end)|>IO.inspect()
    |> Ecto.Multi.update(
      :update_user,
      Ecto.Changeset.change(user, two_factor_authentication: true, force_reset_twofa: false)
    )
    |> Repo.transaction()|>IO.inspect()
    |> case do
      {:ok, _} ->
        {:ok, :updated}

      {:error, _, changeset, _} ->
        error_string = get_first_changeset_error(changeset)
        Logger.error("Error while updating TWOFA: #{error_string}")
        {:error, 41001, error_string}
    end
  end

, вывод должен в основном обновлять таблицу и возвращать два обновленных сообщения fa детали.но в логах показывается ошибка ограничения.пожалуйста, помогите мне с этим .. Я новичок в эликсире.

{:error, :twfa_record_0,
 #Ecto.Changeset<
   action: :insert,
   changes: %{answer: "a", question_id: 1, userprofile_id: 1},
   errors: [
     unique_user_twofa_record: {"has already been taken",
      [constraint: :unique, constraint_name: "user_twofa_detail"]}
   ],
   data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
   valid?: false
 >, %{}}
[error] Error while updating TWOFA: `unique_user_twofa_record` has already been taken

Ответы [ 2 ]

1 голос
/ 11 июня 2019

Вы писали:

вывод должен в основном обновлять таблицу и возвращать два обновленных подробных сообщения.

Но код возвращается:

 #Ecto.Changeset<
   action: :insert,
   changes: %{answer: "a", question_id: 1, userprofile_id: 1},
   errors: [
     unique_user_twofa_record: {"has already been taken",
      [constraint: :unique, constraint_name: "user_twofa_detail"]}
   ],
   data: #Accreditor.TwoFaDetailsApi.TwoFaDetails<>,
   valid?: false
 >

Посмотрите, как написано action: :insert. Таким образом, вы не обновляете, а вставляете, что объясняет ошибку.

insert_or_update обновит запись, только если запись была загружена из базы данных. В своем коде вы создаете записи с нуля, и поэтому они всегда будут вставкой. Вам нужно использовать Repo.get или аналогичный для извлечения их перед передачей их в набор изменений, чтобы вы могли наконец вызвать insert_or_update.

0 голосов
/ 11 июня 2019

Я попытался сделать это, используя upserts для ecto, и это сработало.Вот фрагмент кода для ссылки

Ecto.Multi.insert_or_update(acc, String.to_atom("twfa_record_#{id}"), record,
       on_conflict: :replace_all_except_primary_key,
       conflict_target: [:userprofile_id, :question_id] )
...