File upload in changeset - is this code wrong?

I upload a file in the changeset function like so:

schema "questions" do
  field :media, :string
  field :text, :string
  timestamps(type: :utc_datetime)
end

def changeset(question, attrs) do
  question
  |> cast(attrs, [:text])
  |> validate_required([:text])
  |> add_media_path(attrs["media"])
end

# will upload the file in the right directory
# and set the path in the :media field
defp add_media_path(changeset, %Plug.Upload{} = upload) do
  put_change(changeset, :media, Uploader.upload(upload))
end

defp add_media_path(changeset, _), do: changeset

Is this code wrong? If so, why?
I think I’ll have a problem as soon as I want to make the media field required?

Another possibility is to upload the file from the context:

def create_question(attrs \\ %{}) do
  %Question{}
  |> Question.changeset(attrs)
  # TODO: upload file if changeset is valid
  |> Repo.insert()
end

But then how can I upload the file only if the changeset is valid?

First, you don’t actually need to pass it in like this, you can ‘cast’ it in then modify it in your validator.

However, the issue is you want both the upload and the database entry to succeed or both fail, thus you should probably be using a transaction, I.E. Ecto.Multi, or something out-of-band like the Sage library for a bit more efficiency.

1 Like