Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ecto: Casting %Plug.Upload to virtual field for file upload validations

I would like to be able to trigger a file upload only when a changeset is valid and contains a file.

Is it possible/A good idea to cast a %Plug.Upload e.g

def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> save_image
end

defp save_image(changeset) do
  case changeset do 
    %Ecto.Changeset{valid?: true, changes: %{image: image}} ->
      %{ "url" => "http://" <> image_url } = Cloudinary.upload(image)
      put_change(changeset, :image_url, image_url)
    _ -> 
     changeset
 end
end

I have tried to set :image to a virtual field with type :map but it won’t cast the ` %Plug.Upload

schema "model" do
  field :image_url, :string
  field :image, :map, virtual: true
  timestamps
end

This just cause a validation error with the message Image is invalid

like image 642
Aaron Dufall Avatar asked Nov 17 '15 05:11

Aaron Dufall


2 Answers

Why not define the following function (in a separate module):

def save_image(model, params) do
  changeset = Model.changeset(model, params)
  case changeset.valid? do
    true ->
      %{ "url" => "http://" <> image_url } = Cloudinary.upload(params["file"])
      Model.image_changeset(changeset, image)
    _ -> changeset
  end
end

And have a separate function in your model just for the image changeset.

def image_changeset(changeset, image_url) do
  put_change(changeset, :image_url, image_url)
end
like image 125
Gazler Avatar answered Oct 17 '22 14:10

Gazler


This will work just find be setting the type of the virtual field to :any

schema "model" do
  field :image_url, :string
  field :image, :any, virtual: true
  timestamps
end

Now when all the required fields are present and there is a file it will upload it to the cloud otherwise it will just create/update the fields as normally without triggering an upload if no image file was selected

like image 4
Aaron Dufall Avatar answered Oct 17 '22 13:10

Aaron Dufall