Is there any way to do the inverse to preload?
%Post{
comments: []
}
posts = Repo.all(Post) |> Repo.unload(:comments)
%Post{
comments: #Ecto.Association.NotLoaded<association :comments is not loaded>,
}
Preload is a part of Ecto. Query module that provides the Query DSL. Preload is a powerful tool that allows you to avoid N+1 queries from negatively affecting our performance. We must explicitly preload the data that we are accessing for an association between data.
Queries are used to retrieve and manipulate data from a repository (see Ecto. Repo ). Ecto queries come in two flavors: keyword-based and macro-based. Most examples will use the keyword-based syntax, the macro one will be explored in later sections.
Ecto is an official Elixir project providing a database wrapper and integrated query language. With Ecto we're able to create migrations, define schemas, insert and update records, and query them. Changesets. In order to insert, update or delete data from the database, Ecto. Repo.
Ecto.Association.NotLoaded
is a plain old simple struct, so you might relatively easy implement this unpreload
youself:
defmodule Unpreloader do
def forget(struct, field, cardinality \\ :one) do
%{struct |
field => %Ecto.Association.NotLoaded{
__field__: field,
__owner__: struct.__struct__,
__cardinality__: cardinality
}
}
end
end
And use it later as:
Unpreloader.forget(%Post{....}, :comments)
if you need to compare 2 structs in tests, it's possible to create a comment without preloaded post
association by specifying post_id
field directly:
post = insert!(:post)
comment = insert!(:comment, post_id: post.id)
# instead of
# comment = insert!(:comment, post: post)
or else if you don't need comments
association in post, just create post and its comments separately:
post = insert!(:post)
comment = insert!(:comment, post_id: post.id)
# instead of
# post = insert!(:post, comments: [build(:comment)])
Answering the actual question from comments:
The issue is I am receiving in a test an object which already has preloaded an association and I want to test it with a library which isnt preloading the association and I cannot assert post1 == post2 if just one of them has the comments preloaded
If everything else is the same, I'd just delete that field before asserting:
assert Map.delete(post1, :comments) == Map.delete(post2, :comments)
or if you want to delete more than one field:
fields = [:comments, :users]
assert Map.drop(post1, fields) == Map.drop(post2, fields)
Just wrote a cleaner solution to this today that can dynamically build the %Ecto.NotLoaded{}
struct using Ecto's schema reflection:
defmodule UnPreloader do
def clear_associations(%{__struct__: struct} = schema) do
struct.__schema__(:associations)
|> Enum.reduce(schema, fn association, schema ->
%{schema | association => build_not_loaded(struct, association)}
end)
end
defp build_not_loaded(struct, association) do
%{
cardinality: cardinality,
field: field,
owner: owner,
} = struct.__schema__(:association, association)
%Ecto.Association.NotLoaded{
__cardinality__: cardinality,
__field__: field,
__owner__: owner,
}
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With