2016-08-03 38 views
0

我有這樣一個模式:爲什麼在Ecto模型模式中默認值的函數只評估一次以及如何解決?

defmodule Ticketing.User do 
    use Ticketing.Web, :model 

    schema "users" do 
    field :first_name, :string 
    field :last_name, :string 
    field :email, :string 
    field :hashed_password, :string 
    field :password, :string, virtual: true 
    field :active, :boolean, default: false 
    field :activation_key, :string, default: Ecto.UUID.generate 
    field :description, :string 

    timestamps() 
    end 

    @doc """ 
    Builds a changeset based on the `struct` and `params`. 
    """ 
    def changeset(struct, params \\ %{}) do 
    struct 
    |> cast(params, [:first_name, :last_name, :email, :hashed_password, :password, 
     :active]) 
    |> validate_required([:first_name, :last_name, :email, :password]) 
    end 
end 

所有新用戶擁有相同的UUID。模式中的默認值只評估一次?我應該在changeset而不是schema中實現默認值嗎?

+3

我不確定替代方案是什麼,但是,':default'是在編譯時評估的。請參閱[Schema.field/3](https://hexdocs.pm/ecto/Ecto.Schema.html#field/3)。 –

+0

@MickMacCallum非常好的一點。所以這解釋了爲什麼!謝謝。 – Farsheed

回答

2

贊@Mick MacCallum說,:default是在編譯時在您的代碼中進行評估。如果您使用PostgreSQL,您可以通過將數據庫中列的默認值更改爲gen_random_uuid()來解決此問題。這將使PostgreSQL本身爲每條記錄生成一個隨機的UUID值。 gen_random_uuid()需要pgcrypto PostgreSQL擴展。

生成一個新的遷移:

$ mix ecto.gen.migration add_default_to_users_activation_key 

,代之以新創建的文件中的內容:讓activation_key

defmodule MyApp.Repo.Migrations.AddDefaultToUsersActivationKey do 
    use Ecto.Migration 

    def up do 
    execute "CREATE EXTENSION IF NOT EXISTS \"pgcrypto\";" 
    alter table(:users) do 
     modify :activation_key, :uuid, default: fragment("gen_random_uuid()") 
    end 
    end 

    def down do 
    alter table(:users) do 
     modify :activation_key, :uuid 
    end 
    end 
end 

你也應該在web/models/user.ex添加read_after_writes: truefield聲明activation_key插入後從數據庫返回:

schema "users" do 
    field :activation_key, Ecto.UUID, read_after_writes: true 
end 

演示:

iex(1)> %MyApp.User{name: "John Doe"} |> MyApp.Repo.insert! 
[debug] QUERY OK db=9.6ms 
INSERT INTO "users" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id", "activation_key" ["John Doe", {{2016, 8, 4}, {7, 38, 14, 0}}, {{2016, 8, 4}, {7, 38, 14, 0}}] 
%MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, 
activation_key: "921b1599-fc74-48d7-b74a-b4ce4d8b1333", id: 1, 
inserted_at: #Ecto.DateTime<2016-08-04 07:38:14>, name: "John Doe", 
updated_at: #Ecto.DateTime<2016-08-04 07:38:14>} 
+0

如果我需要一個自定義的UUID生成器會怎麼樣?還是自定義函數生成一些其他值? – Farsheed

+0

您必須將其添加到您的'changeset'函數中。請參閱https://hexdocs.pm/ecto/Ecto.Changeset.html#put_change/3和https://hexdocs.pm/ecto/Ecto.Changeset.html#update_change/3。 – Dogbert

-1

您可以在類型更改爲Ecto.UUID,並設置:autogenerate選項設置爲true。不是100%確定這是否可行,但值得一試:)

相關問題