I’m struggling to understand the purpose of has many through and the many to many associations. The 2 things I’m struggling with is inserting an associated record and querying through an association.
This is my schema setup:
schema "users" do
field(:email, :string)
field(:password, :string)
has_many :users_profiles, UserProfile
has_many :profiles, through: [:users_profiles, :profiles]
end
schema "profiles" do
field(:description, :string)
field(:is_active, :boolean)
has_many :users_profiles, UserProfile
has_many :users, through: [:users_profiles, :users]
end
schema "users_profiles" do
field(:role, :string)
belongs_to(:user, User)
belongs_to(:profile, Profile)
end
Inserting:
When inserting a profile for a user a record needs to be added to the join table users_profiles. It seems to me that ecto doesn’t help here I couldn’t use put_assoc
, cast_assoc
or build_assoc
so my solution was to use Ecto.Multi
. Is this the correct way?
def create_profile(%User{} = user, attrs \\ %{}) do
Multi.new()
|> Multi.insert(:profile, Profile.changeset(%Profile{}, attrs))
|> Multi.run(:user_profile, fn _, %{profile: %{id: id}} ->
UsersProfiles.create_user_profile_owner(%{user_id: user.id, profile_id: id})
end)
|> Repo.transaction()
end
Querying:
When querying using the association via Repo.preload
it causes 2 queries. First way uses the has_many :profiles, through: [:users_profiles, :profiles]
user |> Repo.preload(:profiles)
or alternatively using the has_many :users_profiles
association and the belongs_to
association on users_profiles
schema.
user |> Repo.preload(users_profiles: [:profile])
So I instead just wrote a join query as that way it would only be a single query, like so:
def profiles(user) do
query =
from p in Profile,
join: up in UserProfile,
on: up.profile_id == p.id,
where: up.user_id == ^user.id
Repo.all(query)
end
So I haven’t used the associations so far so I’m not sure of their purpose or maybe I’m doing things incorrectly?