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

несколько дней назад я начал использовать 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-столбец для модели, я получаю сообщение об ошибке, что столбец уже существует. Если я пренебрегаю set primary_key false в table/2 и удалить id столбец, таблица генерируется с последовательным idколонки.

мне нужно вручную установить id в набор изменений, или Я сделал ошибку в настройке моего приложения для использования UUIDs? Заранее спасибо

2 ответов


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

Ecto v2

обработка UUIDs в 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}

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

Ecto v1

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

прежде чем мы двинемся дальше, важно сказать, что вы используете :uuid это вернет двоичные файлы вместо читаемых человеком UUIDs. Очень вероятно, что вы хотите использовать 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 является обратным вызовом, и он вызовет данный модуль в данной функции с заданными аргументами, с набором изменений, представляющим то, что вставляется в качестве первого аргумента.

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


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

mix phx.new project_name --binary-id