How to change a field only when inserting in Ecto

Hi everyone!
I’m trying to generate the uuid field of a WorkProject schema only when inserting a WorkProject to the database, with the following:

  def generate_random_uuid(length) do
    :crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length)
  end

  def convert_markdown(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{description_markdown: description}} ->
        put_change(changeset, :description_html, Earmark.as_html!(description))
      _ ->
        changeset
    end
  end

  def generate_uuid(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, action: :insert} ->
        put_change(changeset, :uuid, generate_random_uuid(12))
      _ ->
        changeset
    end
  end

  @doc false
  def changeset(work_project, attrs) do
    work_project
    |> cast(attrs, [:name, :short_description, :description, :description_html, :description_markdown, :creator_id, :work_status, :public_status, :start_date, :end_date, :project_version_id, :repo_url, :show_url])
    |> validate_required([:name, :creator_id, :work_status, :public_status, :start_date, :project_version_id])
    |> foreign_key_constraint(:creator_id)
    |> foreign_key_constraint(:project_version_id)
    |> convert_markdown
    |> generate_uuid
  end
end

The problem is that when I pattern match the action to :insert it doesn’t work and if I don’t pattern match it at all then it generates a different uuid every time we change a WorkProject

You can check if uuid exists already…

case get_field(changeset, :uuid) do
  nil -> changeset |> put_change(:uuid, generate_random_uuid(12))
  _ -> changeset
end
1 Like

Don’t know how that didn’t occur to me :sweat_smile:

Thank you @kokolegorille