CherryMan

CherryMan

Automatically creating has_one child on parent creation

Say I have the following models

import Ecto.Changeset
import Ecto.Schema

defmodule Parent do
  schema "parents" do
    has_one :child, Child
  end

  def changeset(%Parent{} = ch, attrs) do
    ch
    |> cast(attrs, [])
  end
end

defmodule Child do
  schema "childs" do
    belongs_to :parent, Parent
  end

  def changeset(%Child{} = ch, attrs) do
    ch
    |> cast(attrs, [:parent_id])
    |> foreign_key_constraint(:parent_id)
  end
end

Whenever I create a Parent, I want a Child to be created and associated with the Parent automatically. Whenever I update Parent, I only want the Child to be updated if it is a part of the attrs or changeset given to Parent.changeset/2. Otherwise, the changeset should silently work.

Basically, Child should only be part of the changeset if it is being updated or if Parent is being created.

I’ve tried playing around with cast_assoc and put_assoc but neither seems to work the way I want it to.

Marked As Solved

CherryMan

CherryMan

Thanks for your help @dimitarvp. Final solution:

defmodule Parent do
  schema "parents" do
    has_one :child, Child, on_replace: :update
  end

  def changeset(%Parent{} = ch, attrs) do
    ch
    |> cast(attrs, [])
    |> cast_assoc(:child)
    |> changeset_preload(:child)
    |> put_assoc_nochange(:child, %{})
  end

  def changeset_preload(ch, field),
    do: update_in(ch.data, &Repo.preload(&1, field))

  def put_assoc_nochange(ch, field, new_change) do
    case get_change(ch, field) do
      nil -> put_assoc(ch, field, new_change)
      _ -> ch
    end
  end
end

Also Liked

dimitarvp

dimitarvp

I suggest you use get_change. If you use get_field then you will not detect if a child has been supplied in the attrs variable.

dimitarvp

dimitarvp

Yes it is:

defmodule Parent do
  # ...
  def changeset(%Parent{} = ch, attrs) do
    cs =
      ch
      |> cast(attrs, [])
      |> cast_assoc(:child, required: true)

    # Conditionally set `child` unless it's already set in the parameters.
    case Ecto.Changeset.get_change(cs, :child) do
      nil ->
        Ecto.Changeset.put_assoc(cs, :child, %Child{"your_desired_default_child_fields_go_here"}

      _ ->
        cs
    end
  end

In short, no magic. You can make it implicit by encoding that behaviour in the default changeset function.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement