Настройка 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