New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Propagate opts when process child #3309
Comments
Post is an association on the User, right? Internal operations like inserting association don't go through the main API (that you're overriding) so that's why it doesn't work. Your best bet is using |
@wojtekmach If it doesn't go though the main API that I am overriding then why it is called twice? [foobar: :enable] #Ecto.Changeset<action: nil, changes: %{posts: [#Ecto.Changeset<action: :insert, changes: %{}, errors: [], data: #EctoOpts.Post<>, valid?: true>]}, errors: [], data: #EctoOpts.User<>, valid?: true>
[] #Ecto.Changeset<action: :insert, changes: %{user_id: 7}, errors: [], data: #EctoOpts.Post<>, valid?: true> |
you showed how you're inserting user, could you show how you're inserting the post? |
@wojtekmach I am not inserting the post manually :) it is a part of user_attrs = %{
"posts" => [
%{}
]
}
%EctoOpts.User{}
|> EctoOpts.User.changeset(user_attrs)
|> Repo.insert(foobar: :enable) User.ex defmodule EctoOpts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
has_many :posts, EctoOpts.Post
end
def changeset(struct, attrs) do
struct
|> cast(attrs, [])
|> cast_assoc(:posts)
end
end Post.ex defmodule EctoOpts.Post do
use Ecto.Schema
import Ecto.Changeset
schema "posts" do
belongs_to :user, EctoOpts.User
end
def changeset(struct, attrs) do
struct
|> cast(attrs, [])
end
end |
never mind, you're using associations. yeah, we don't guarantee that the options are passed through down to associations. |
Right. We don’t guarantee neither insert (It May work in some cases) will be called nor pass the options downstream. It is easier if you tell us generally about your use case. |
Right! Why guarantee? It is just about pass the options. Are all nested assoc inserts done within same process? Barely viewing the code seems it is. |
@josevalim Just did it :) |
you're still not telling us your use case, what options do you want to pass - what will you do with them? |
Hmmm # Main app repo
defmodule MyApp.Repo do
use Ecto.Repo ...
use MyTracker.Repo
end
# An extension to override main repo
defmodule MyTracker.Repo do
defmacro __using__(opts) do
defoverridable([insert: 2])
defp do_tracking(%Ecto.Changeset{} = cs) do
# Basicly get .changes and store them(somewhere).
end
def insert(struct, opts) do
if Keyword.get(opts, :tracking_enabled, false) do
do_tracking(struct)
end
super(struct, opts)
end
end
end
# Expects to create User in my Repo, track User and all nested structs downside the changes tree
def do_something_important_here1(attrs) do
MyApp.User
|> MyApp.User.changeset(attrs)
|> MyApp.Repo.insert([tracking_enabled: true])
end
# Expects to do not track anything and User is created
def do_something_important_here2(attrs) do
MyApp.User
|> MyApp.User.changeset(attrs)
|> MyApp.Repo.insert()
end |
We were used to pass all options but some actions caused issues when passed down, such as :on_conflict and returning, so we made it opt-in. Generally speaking, your pattern of overriding |
@josevalim thanks for the answer! Seems it would be legal to inject aka inherit |
Still curios about the guarantee when associated insert isn't called. Does it go through associations and just applies exactly the same steps as parent's changeset? |
We call it for insert but you can see some association operations, such as delete_all and update_all, bypass the repo. But the point is we cannot guarantee this will always work, the fact we do call it is just a convenient implementation detail. |
The first issue I see is how to track deletion, the plan is to query inside overridden I assume that when you mentioned May be now I started to understand what you were trying to say. If "guarantee" is about it could break your logic later because of some internal changes then alright, it is time to take |
Environment
Current behavior
If I try to inject Repo and want to extend "Shared options" passed to the repo to apply it for each insert/update/delete statements then it is not passed to child inserts
Having my repo like this
and having the test like
then I get next output
Is it works like this for some reason? In my project I made a workaround playing with
Process.put
/Process.get
combo to propagate the setting for child inserts and then restore the state after action was done, but looks dirty imho.Expected behavior
The
opts
are available recursively to each action callThe text was updated successfully, but these errors were encountered: