Reputation: 1096
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
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