silverdr
Has_many through load selected associated columns
Building on the example from another thread:
https://forum.elixirforum.net/t/many-to-many-once-again-extra-data-on-the-join-table/
User has many Chatroom through UsersChatrooms.
Now, I would like to get names and ids of given user’s chatrooms. I surely can do for example:
user = Repo.preload(user, :chatrooms)
but this will load “everything” from both final and join tables, while I’d rather do something like Rails’ pluck or at least select so that only columns I am interested in are queried. What are the “correct” ways of doing this with Ecto associations?
Alternatively already while fetching the user record
Marked As Solved
LostKobrakai
You can use ecto query to select what you need. There’s not really a high level API for that in ecto.
query =
from cr in Chatroom,
join: ucr in assoc(:users_chatrooms),
on: ucr.user_id == ^user_id and ucr.chatroom_id == cr.id,
select: map(cr, [:id, :name])
Repo.all(query)
or
user
|> Ecto.assoc(:chatrooms)
|> select([cr], map(cr, [:id, :name])
|> Repo.all()
Also Liked
LostKobrakai
Ecto only fetches the fields present in the schema anyways. You could even have multiple schemas for a single table, but different use cases. If you’re hardly fetching all fields of a schema I’d wonder if the schema is well defined or if you’d not rather want to split it into multiple.
LostKobrakai
Ecto is far more geared towards giving developers the ability to deal with the sql. It’s not an ORM and doesn’t try to be. One could probably built one on top of ecto. I can see this not being great when trying to get things of the ground, but it’s really great once you need to maintain that thing flying.
dimitarvp
Yep, more or less. And it’s important to remember that Ecto is explicit: it tries very hard not to do things you didn’t ask it to do.
dimitarvp
I sympathize with the need. You can take it one step further by making helpers for it in your context/domain modules, e.g. with the second coding snippet that @LostKobrakai showed you like so:
defmodule MyApp.Users do
def with_chatroom_names(user) do
user
|> Ecto.assoc(:chatrooms)
|> select([cr], map(cr, [:id, :name])
|> Repo.all()
end
end
And it becomes intuitive to read the consuming modules when they do stuff like Users.with_chatroom_names(user) |> do_other_stuff() …IMO anyway; naming is hard).
I was a Rails dev too, long ago (for 6.5 years). I still sometimes miss bits and pieces from it but as time went by I gravitated towards preferring to get my hands a little more dirty for the added peace of mind that when looking at the app’s code then what I see is exactly what I get.
But again, if you find that clunky, nothing stops you from emulating a few favourite Rails-like idioms in your projects, or even spin them off to internal company libraries (I’ve done so in the past).
sodapopcan
I went a little too far with my answer by extracting it to a function when obviously @LostKobrakai 's a way better version. My point was just that Ecto is much faster than ActiveRecord (when you are creating AR objects) so it’s not the end of the world to grab all the fields but that is really neither here nor there.
Moving on from pluck, from what you wrote I get the impression you haven’t grasped the full power of composability. You certainly shouldn’t be needing to write SQL first to turn it into Ecto with any regularity. I’ve found Ecto to be incredibly concise and I don’t miss AR even a little. A lot of reusable little one line query helpers can go a really long way. I would have to see examples of what you’re doing, though (not suggesting you want to share, of course
)







