I'd like to have a unique field in an Ecto model. This field should contain a random string which I can generate easily (for example, see here). However, I would like to avoid to generate the string and check if it's already present in the database, as that would expose me to race conditions.
I'd like to have it retry insertion until a unique string is found. But how do I it? Should it be inside the changeset/2
function?
defmodule LetsPlan.Event do
use LetsPlan.Web, :model
schema "events" do
field :name, :string
field :from, Ecto.DateTime
field :to, Ecto.DateTime
field :slug, :string
timestamps
end
@required_fields ~w(from to)
@optional_fields ~w(slug)
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> unique_constraint(:slug)
end
end
It's been 4 months, so I guess you figured it out. You should create different changeset depending the action you are doing and a base changeset for "read" purposes.
Explicit > Implicit
Your model could end up like that:
defmodule App.Classified do
@rules_create %{
:required_fields => ~w(tenant_id firstname lastname email password password_confirmation phone birthday description),
:optional_fields => ~w(),
}
@rules_update %{
:required_fields => ~w(firstname lastname email phone birthday description),
:optional_fields => ~w()
}
def changeset(model, params \\ :empty) do
model
|> cast(params, [], [])
end
@doc """
Changeset when you create a new classified
"""
def create_changeset(model, params \\ :empty) do
model
|> cast(params, @rules_create.required_fields, @rules_create.optional_fields)
|> validate_length(:description, min: 280)
|> validate_length(:password, min: 6)
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> hash_password
|> make_token
|> make_search
end
@doc """
Changeset when you update an classified
"""
def update_changeset(model, params \\ :empty) do
model
|> cast(params, @rules_update.required_fields, @rules_update.optional_fields)
|> validate_length(:description, min: 280)
|> make_search
end
end