I have Beta::Groups
and Beta::Users
.
Users
can belong to more than one Group
, but each Beta::User
is also a regular User
.
class Beta::User < ApplicationRecord
belongs_to :beta_group, class_name: "Beta::Group"
belongs_to :user
validates :user_id, uniqueness: { scope: :beta_group_id }
end
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
Effectively, Beta::User
is just a join table between Beta:Group
and User
When I load a Beta::Group
, how can I retrieve the data for all the user's in that group?
@beta_group = Beta::Group.find(beta_user_params[:beta_group_id])
@beta_users = @beta_group.beta_users.all
The last line only retrieves the beta_users data obviously (just the beta_group_id and user_id). I don't want to have to then iterate through all the user_id's to get the actual User's full data.
So how I can set this up so that I can do something like @beta_group.users.all
to retrieve all the data for each user that is a Beta:User
?
EDIT - What I have tried
I tried adding this to the Beta::Group
model:
has_many :users, through: :beta_users, source: :user
But when I call beta_group.users the query that runs is as follows, returning an empty array:
SELECT "beta_users".* FROM "beta_users" INNER JOIN "beta_users" "beta_users_users" ON "beta_users"."id" = "beta_users_users"."user_id" WHERE "beta_users_users"."beta_group_id" = $1 [["beta_group_id", 1]]
Notice that it is not joining correctly. It should be trying to join on "beta_users"."user_id" to "users.id"
CodePudding user response:
I believe you can add a has_many through association:
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
# here:
has_many :users, through: :beta_users, source: :user
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
Then you should be able to call the association:
@beta_group = Beta::Group.find(beta_user_params[:beta_group_id])
@beta_users = @beta_group.users
EDIT - I believe you have an issue with scopes and the class_name because you have a User inside the Beta module.
Try adding the class_name: "::User"
to both associations:
class Beta::User < ApplicationRecord
belongs_to :beta_group, class_name: "Beta::Group"
belongs_to :user, class_name: "::User" # without class_name it'll try a self association
end
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
has_many :users, class_name: "::User", through: :beta_users # without a class_name it'll try to join the Beta::User
end
CodePudding user response:
The problem was due to the fact that User
is a table in both the top-level namespace and inside the Beta
namespace.
Therefore, it is necessary to inform rails which table to use to create the SQL for, by specifying the top-level class in the relation, like this:
class_name: '::User'
Final code:
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: 'Beta::User', foreign_key: 'beta_group_id'
has_many :users, through: :beta_users, source: :user, class_name: '::User'
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
which generates the correct SQL:
SELECT "users".* FROM "users" INNER JOIN "beta_users" ON "users"."id" = "beta_users"."user_id" WHERE "users"."viewer" = $1 AND "beta_users"."beta_group_id" = $2 [["viewer", false], ["beta_group_id", 1]]