Подтвердить что ты не робот

Настройка Phoenix Framework и Ecto для использования UUID: как вставить сгенерированное значение?

Несколько дней назад я начал использовать Elixir и Phoenix Framework (v 0.12.0) с базой данных Postgres. Я пытаюсь создать таблицу с первичным ключом UUID, который я предпочитаю по последовательному по умолчанию.

После использования mix phoenix.gen.html для создания файлов модели и миграции и последующих действий в документах Phoenix я изменил

def model do
    quote do
      use Ecto.Model
    end
  end

в web.ex до

def model do
  quote do
    use Ecto.Model
    @primary_key {:id, :uuid, []}
    @foreign_key_type :uuid
  end
end

как указано в документах Ecto. Я также изменил миграцию на

create table(:tblname, primary_key: false) do
  add :id, :uuid, primary_key: true
  [other columns]
end

К сожалению, когда я пытаюсь добавить запись в таблицу из автоматически сгенерированной формы, я получаю сообщение об ошибке, потому что id имеет значение null. Если я вручную добавлю id -column в модель, я получаю сообщение о том, что столбец уже существует. Если я пренебрег установкой primary_key на false в table/2 и удалит столбец id, таблица будет создана с последовательным id -column.

Нужно ли вручную установить id в наборе изменений или я сделал ошибку при настройке моего приложения для использования UUID? Спасибо заранее

4b9b3361

Ответ 1

EDIT: я обновил этот ответ на Ecto v2.0. Вы можете прочитать предыдущий ответ в конце.

Ecto v2

Обработка UUID в Ecto стала намного более прямой с момента первоначального ответа. Ecto имеет два типа идентификаторов: :id и :binary_id. Первый - это целочисленный идентификатор, который мы знаем из баз данных, второй - двоичный файл базы данных. Для Postgres это UUID.

Чтобы иметь UUID в качестве первичных ключей, сначала укажите их в своей миграции:

create table(:posts, primary_key: false) do
  add :id, :binary_id, primary_key: true
end

Затем в вашем модуле модели (вне блока schema):

@primary_key {:id, :binary_id, autogenerate: true}

Когда вы укажете опцию :autogenerate для :binary_id, Ecto гарантирует, что либо адаптер, либо база данных сгенерируют его для вас. Однако вы можете создать его вручную, если хотите. Btw, вы могли бы использовать :uuid в своей миграции и Ecto.UUID в вашей схеме вместо :binary_id, преимущество :binary_id в том, что оно переносимо через базы данных.

Ecto v1

Вы должны сообщить своей базе данных, как автоматически генерировать UUID для вас. Или вам нужно создать его со стороны приложения. Это зависит от того, какой вы предпочитаете.

Прежде чем двигаться дальше, важно сказать, что вы используете :uuid, который будет возвращать двоичные файлы вместо человекообразных UUID. Скорее всего, вы захотите использовать Ecto.UUID, который отформатирует его как строку (aaaa-bbb-ccc -...) и что я буду использовать ниже.

Генерация в базе данных

В вашей миграции укажите значение по умолчанию для поля:

add :id, :uuid, primary_key: true, default: fragment("uuid_generate_v4()")

Я предполагаю, что вы работаете на PostgreSQL. Вам необходимо установить расширение uuid-ossp с помощью CREATE EXTENSION "uuid-ossp" в pgAdmin или добавить execute "CREATE EXTENSION \"uuid-ossp\"" в перенос. Более подробную информацию о генератор UUID можно найти здесь.

Обратно в Ecto, в вашей модели, попросите Ecto прочитать поле из базы данных после вставки/обновления:

@primary_key {:id, Ecto.UUID, read_after_writes: true}

Теперь, когда вы вставляете, база данных будет генерировать значение по умолчанию, а Ecto будет читать его.

Создание в приложении

Вам нужно будет определить модуль, который вставляет UUID для вас:

defmodule MyApp.UUID do
  def put_uuid(changeset) do
    Ecto.Changeset.put_change(changeset, :id, Ecto.UUID.generate())
  end
end

И используйте его как обратный вызов:

def model do
  quote do
    use Ecto.Model
    @primary_key {:id, Ecto.UUID, []}
    @foreign_key_type Ecto.UUID
    before_insert MyApp.UUID, :put_uuid, []
  end
end

before_insert является обратным вызовом, и он будет вызывать данный модуль в данной функции с заданными аргументами, а набор изменений, представляющий то, что вводится, задается как первый аргумент.

Это все. Кстати, есть шанс, что это будет более оптимизировано в будущем.:)

Ответ 2

Также при создании новой опции пропуска проекта --binary-id для использования UUID в качестве первичного ключа по умолчанию. (Запуск Ecto v2)

mix phx.new project_name --binary-id