gorav
Validation on association set
I have a registrations model and participants model as follows:
schema "registrations" do
field :conf_number, :string
field :status, :integer, default: 1
belongs_to :event, Event
has_many :participants, Participant
timestamps()
end
schema "participants" do
field :accommodation_choice, :integer, default: 1
field :age, :integer
field :amount_paid, :integer, default: 0
field :arrival_time, :integer, default: 1
field :city, :string
field :conf_number, :integer
...
field :primary, :boolean, default: false
...
field :admin_comment, :string
belongs_to :registration, Registration
belongs_to :country, Country
belongs_to :payment, Payment
timestamps()
end
I am using nested resources to save the registration with participants. I want to include a validation in changeset that there should be one participant with primary boolean as true while saving the registration. What is the best way to achieve this?
I have create_changeset and changeset (for update) with some logic running but both of them call the following common_changeset in the end:
defp common_changeset(changeset) do
changeset
|> validate_required([:conf_number, :event, :status])
|> cast_assoc(:participants, required: true)
|> unique_constraint(:conf_number)
end
I want to be able to add something like check_primary method to the common_changeset pipeline as last step. Thanks.
Most Liked
gorav
Solved the first requirement as follows:
@spec check_primary(Ecto.Changeset.t()) :: Ecto.Changeset.t()
defp check_primary(%{valid?: true} = changeset) do
registration = apply_changes(changeset)
case Enum.count(registration.participants, fn participant -> participant.primary end) do
1 -> changeset
_ -> add_error(changeset, :base, "One primary registrant required")
end
end
defp check_primary(changeset), do: changeset
not sure if this is the best approach though. @idi527 thanks for all your help.
idi527
Maybe something like Ecto.Multi might help. It would look like this
alias Ecto.Multi
@spec create_registration(%{binary => term} | %{atom => term}) :: {:ok, %{registration: %Registration{}, check_primary: :ok}} | {:error, :registration | :check_primary, Ecto.Changeset.t | :error, changes_so_far :: %{optional(atom) => term}}
def create_registration(attrs) do
Multi.new()
|> Multi.insert(:registration, Registration.changeset(%Registration{}, attrs))
|> Multi.run(:check_primary, fn %{registration: registration} -> check_primary(registration) end)
|> Repo.transaction()
end
which would rollback if check_primary(registration) returns :error.
Checking them separately does not help.
Why? I thought it’d work … Can you show the code that you’ve used?
I need to compute fees for the participant but that can happen only after arrival_date and departure_date changes, if any, are applied to the record
That also could be done either in a multi or through a changeset, I prefer changesets:
@spec put_fees(Ecto.Changeset.t) :: Ecto.Changeset.t
defp put_fees(%{valid?: true, changes:%{arrival_date: arrival_date, departure_date: departure_date}} = changeset) do
put_change(changeset, :fees, compute_fees(arrival_date, departure_date))
end
defp put_fees(changeset), do: changeset
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








