web-dev-qa-db-ja.com

UUIDを使用するようにPhoenixFrameworkとEctoを設定する:生成された値を挿入する方法は?

数日前、PostgresデータベースでElixirとPhoenix Framework(v 0.12.0)を使い始めました。 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-列を手動で追加すると、列がすでに存在するというエラーが表示されます。 primary_keytable/2をfalseに設定し、id列を削除しなかった場合、テーブルは連続したid-列で生成されます。

チェンジセットでidを手動で設定する必要がありますか、それともUUIDを使用するようにアプリを設定する際にエラーが発生しましたか?前もって感謝します

21
Theemuts

編集:私はこの回答をEctov2.0に更新しました。最後に前の答えを読むことができます。

Ectov2

EctoでのUUIDの処理は、最初の回答以降、はるかに簡単になりました。 Ectoには、:id:binary_idの2種類のIDがあります。 1つ目はデータベースからわかる整数IDで、2つ目はデータベース固有のバイナリです。 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はアダプターまたはデータベースのいずれかがそれを生成することを保証します。ただし、必要に応じて手動で生成することもできます。ところで、移行では:uuidを使用し、スキーマではEcto.UUIDの代わりに:binary_idを使用することもできます。:binary_idの利点は、データベース間で移植できることです。

Ectov1

UUIDを自動的に生成する方法をデータベースに指示する必要があります。または、アプリケーション側から生成する必要があります。それはあなたがどちらを好むかによります。

先に進む前に、人間が読み取れるUUIDの代わりにバイナリを返す:uuidを使用していることを伝えることが重要です。文字列(aaaa-bbb-ccc -...)としてフォーマットするEcto.UUIDを使用する可能性が非常に高く、これを以下で使用します。

データベースでの生成

移行では、フィールドのデフォルトを定義します。

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

PostgreSQLで実行していることを前提としています。 pgAdminにCREATE EXTENSION "uuid-ossp"を指定してuuid-ossp拡張機能をインストールするか、移行にexecute "CREATE EXTENSION \"uuid-ossp\""を追加する必要があります。 IDジェネレーターはここにあります に関する詳細情報。

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はコールバックであり、指定された関数で指定されたモジュールを指定された引数で呼び出します。挿入されているものを表すチェンジセットが最初の引数として指定されます。

それがすべてのはずです。ところで、これは将来さらに合理化される可能性があります。 :)

43
José Valim

また、新しいプロジェクトパスオプションを作成する場合--binary-id UUIDをデフォルトの主キーとして使用します。(開始Ecto v2

mix phx.new project_name --binary-id
6
coderVishal