Reputation: 144
So I have User&Role models which is linked to UserRole model using has_many :through association. And im using a serializer for User. Note: The user has many roles. But i'm not able to get all the roles in the serializer for some reason. This is the query,
unless params['roles'].blank?
render json: User.includes(:roles)
.references(:user_roles)
.where('roles.name IN (?)', params['roles'])
else
render json: User.all
end
And in my User serializer I have
attributes :id, :name, :email, :roles
def roles
object.roles.pluck(:name)
end
This thing is, I have a user with both "admin","author" roles. When I pass "admin" as params, Output json object just has roles: ["admin"]
for that user. Upon debugging, for this object, object.roles.count
is 2 BUT when I do object.roles
, it shows only 1 record. What's happening?
When I do User.find(object.id).roles.pluck(:name)
, this works. But this runs queries in the serializer loop..
The output response is perfectly fine for User.all withOUT params. (getting roles: ["author", "admin"]). The problem is when I pass a param. Perhaps something wrong with my query?
Upvotes: 0
Views: 456
Reputation: 102036
Since the where clause limits the loaded records you may need to use a subquery:
User.includes(:roles).where(
id: User.joins(:roles).where(roles: { name: params[:roles] })
)
If you look at the SQL generated you can see that the where clause here only applies to the subquery - thus all the associated roles are loaded and not just the ones that match WHERE "roles"."name" = $1
.
User Load (3.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT "users"."id" FROM "users" INNER JOIN "user_roles" ON "user_roles"."user_id" = "users"."id" INNER JOIN "roles" ON "roles"."id" = "user_roles"."role_id" WHERE "roles"."name" IN ($1, $2, $3)) LIMIT $4 [["name", "admin"], ["name", "foo"], ["name", "bar"], ["LIMIT", 11]]
UserRole Load (0.6ms) SELECT "user_roles".* FROM "user_roles" WHERE "user_roles"."user_id" = $1 [["user_id", 1]]
Role Load (0.5ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 [["id", 1]]
Also if you want to use the loaded records you need to use map
not pluck
as pluck by design creates a separate select query.
def roles
object.roles.map(&:name)
end
Upvotes: 1