arunvelsriram
arunvelsriram

Reputation: 1096

Custom changeset validation for insert action alone

Am working on Phoenix project. I have the following model:

defmodule MyApp.Exam do
  use MyApp.Web, :model

  alias Ecto.Changeset
  alias MyApp.Repo
  alias MyApp.TimexHelper
  alias Timex.Date

  import Ecto.Query

  schema "exams" do
      field :start_time, Timex.Ecto.DateTime
      belongs_to :student, Student

      timestamps
  end

  @required_fields ~w(student_id start_time)

  def changeset(model, params \\ :empty) do
      model
      |> cast(params, @required_fields, @optional_fields)
      |> assoc_constraint(:student)
      |> is_in_future(:start_time)
  end

  def is_in_future(changes, field) do
      new_time = Changeset.get_field(changes, field)
      valid = TimexHelper.compare(new_time, Date.now)
      if !valid, do: changes = Changeset.add_error(changes, field, "should be in the future")
      changes
  end
end

How do I make the custom validation is_in_future for the field start_time to be executed only during insert operation ?

I tried adding a if condition inside is_in_future to check if start_time is available. But I don't think that's a better solution.

Am looking for something like:

# Rails model field validation
validates_presence_of :start_time, :on => :create

Upvotes: 0

Views: 514

Answers (1)

Fabi755
Fabi755

Reputation: 1514

I'm not sure, but I think there is no solution like rails. You will create the changeset and validation before your database call will do. After that you insert/update the model by yourself. The cast method has no informations about the database call after your validation.

My idea is to add a parameter like a boolean or an atom to changeset function to define your type of validation.

func changeset(model, params \\ :empty, type \\ :insert) do
  changeset = model
    |> cast(params, @required_fields, @optional_fields)
    |> assoc_constraint(:student)

  case type do
    :insert ->
      is_in_future(changeset, :start_time)
    :update ->
      changeset
  end
end

Then you should call changeset(model, params, :insert) or changeset(model, params, :update).

Hope it helps.

EDIT: An other idea is to checkout if the id of model is set to nil. This is like your idea with the existing start_time field.

Upvotes: 1

Related Questions