halostatue
If I am using cast_embed/3, how do I clear an embeds_many entry?
I have a table flows that has a options JSONB column
CREATE TABLE flows (
id bigint generated always as identity primary key,
options JSONB
);
I have defined the schema as follows:
defmodule Test.Flow do
use Ecto.Schema
import Ecto.Changeset
defmodule Option do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :value, :string
end
def changeset(opts \\ %__MODULE__{}, attrs) do
cast(opts, attrs, [:value])
end
end
schema "flows" do
embeds_many :options, Option, on_replace: :delete
end
def changeset(flows \\ %__MODULE__{}, attrs) do
flows
|> cast(attrs, [:id])
|> cast_embed(:options)
|> validate_length(:options, min: 1)
end
end
In Typescript type terms, the type would look something like this:
interface Flow {
id: number
options?: [Option, ...Option[]] | null
}
interface Option {
value: string
}
That is, options should be null or an array of at least one Option. But I can’t make that happen:
iex(1)> {:ok, f} = Repo.insert(Test.Flow.changeset(%{})
{:ok,
%Test.Flow{
__meta__: #Ecto.Schema.Metadata<:loaded, "flows">,
id: 1,
options: []
}}
iex(2)> Test.Flow.changeset(f, %{options: nil})
#Ecto.Changeset<
action: nil,
changes: %{},
errors: [options: {"is invalid", [validation: :embed, type: {:array, :map}]}],
data: #Test.Flow<>,
valid?: false
>
iex(3)> Repo.update!(f, %{options: [%{}]})
%Test.Flow{
__meta__: #Ecto.Schema.Metadata<:loaded, "flows">,
id: 1,
options: [%Test.Flow.Option{value: nil}]
}
iex(4)> f = Repo.get(TestFlow, 1)
%Test.Flow{
__meta__: #Ecto.Schema.Metadata<:loaded, "flows">,
id: 1,
options: [%Test.Flow.Option{value: nil}]
}
iex(5)> Test.Flow.changeset(f, %{options: []})
#Ecto.Changeset<
action: nil,
changes: %{
options: [
#Ecto.Changeset<action: :replace, changes: %{}, errors: [],
data: #Test.Flow.Option<>, valid?: true>
]
},
errors: [
options: {"should have at least %{count} item(s)",
[count: 1, validation: :length, kind: :min, type: :list]}
],
data: #Test.Flow<>,
valid?: false
>
This feels like it should be possible, if not easy, but I don’t really see a way to do it, especially since there doesn’t appear to be a distinction between attrs of %{} (options is missing) and %{options: nil} (options is explicitly nulled). That is, when I do Test.Flow.changeset(f, %{options: nil}).changes, I get %{}.
I suppose that I could use a sigil value (:none), but that feels…awkward.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 8- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
dimitarvp
I seem to remember people around here saying that
Ecto.Changesetis not treatingnilas an actual change but don’t quote me on that.But in case it’s true, what’s the difficulty in making the empty list the special empty value you’re looking for?
halostatue
Because it doesn’t seem to work. I have added the following to my
.iex.exsfor my project to explore.And this is my experimental session (I just dropped and recreated the table in
psql):But that is incorrect. If the array is empty, the field should be set to
NULL, and that’s clearly not the case:I can change the wire serialization so that an empty array is returned as
null, but it feels odd that anullablefield can no longer be set tonull— and I can’t see any way from thefetch_changeto know that this is going to be cleared. I would have to refer back toattrsto see if I should do anything.sodapopcan
This should be:
IE, don’t include the empty map as that implies you have an option you want to run through a changeset. You just want an empty list meaning “no options.” Otherwise ya, AFAIK, Ecto’s has_many relations are always going to be lists (ie, not null). There was some discussion around this that I can’t find atm.
halostatue
That initial insert was to provide a record that needed to have
[{value: null}]turned intonullthrough clearing to illustrate my problem. The later changeset calls illustrate this more clearly.My issue is with
embeds_many, nothas_many. If I were storing this data in a separate table, this would not be a problem, because the “owned” records would simply be deleted.This feels like something that can and should be handled better. Maybe with something like
embeds_many :field, Type, nullable: true, which would theoretically be able to flag tocast_embed/4that if the value isnil(and not missing), then it should be set tonilin the update.I’m going to need to figure a way around this, because an empty list is explicitly not a valid value. It’s either
nilor a non-empty list.sodapopcan
Sorry, I meant embeds_many—they work the same in that regard. And also I seem to have misread your iex output.
I found the discussion I was thinking of:
halostatue
Thanks for the link.
Yeah. I have explicitly disallowed the empty list in the type definition, which is why this behaviour is…disappointing. I may need to see if there is a way to make
embeds_manyandcast_embedwork properly for this case, because as a few posts in that thread make clear,NULLis not the same as[]and we’re just somewhat lucky that I hadn’t made a constraint on the table which checked fornullorjsonb_array_length(options) > 1(this is still a good idea, but until I can work around this issue, I can’t have it).sodapopcan
You could add your voice to that thread. Not sure if it will do much good but you can always try! My guess is that this just doesn’t come up often enough and there are other ways around it, though I’m not quite sure of the workarounds myself.
halostatue
One workaround is to define an
Ecto.Type. My example here isn’t well organized (the type here should be a different module,OptionList), but that’s fine for an example:It’s a lot of work, and I suspect that some of it could be wrapped in a macro.
I fear that the lesson here is to avoid
embeds_manyif your column is intentionally nullable, because Ecto works against your design in this case.