【问题标题】:Active Model Serializer object not returning all association records (has many: through relationship)活动模型序列化器对象不返回所有关联记录(有很多:通过关系)
【发布时间】:2019-01-10 13:11:39
【问题描述】:

所以我有 User&Role 模型,它使用 has_many 链接到 UserRole 模型:通过关联。我为用户使用序列化程序。注意:用户有很多角色。但由于某种原因,我无法获得序列化程序中的所有角色。这是查询,

unless params['roles'].blank?
  render json: User.includes(:roles)
                    .references(:user_roles)
                    .where('roles.name IN (?)', params['roles'])
else
  render json: User.all
end

在我的用户序列化程序中,我有

attributes :id, :name, :email, :roles

def roles
  object.roles.pluck(:name)
end

这件事是,我有一个同时具有“管理员”、“作者”角色的用户。当我将“admin”作为参数传递时,输出 json 对象只有该用户的 roles: ["admin"]。在调试时,对于这个对象,object.roles.count 是 2,但是当我做 object.roles 时,它只显示 1 条记录。发生了什么事?

当我做User.find(object.id).roles.pluck(:name) 时,这有效。但这会在序列化程序循环中运行查询..

对于没有参数的 User.all,输出响应非常好。 (获取角色:[“作者”,“管理员”])。问题是当我传递一个参数时。也许我的查询有问题?

【问题讨论】:

  • .pluck 将始终创建数据库查询。这就是它的工作原理。如果要使用加载的记录,请使用object.roles.map(&:name)
  • 还有实际想要的结果是什么的问题。由于.includes 创建了一个外连接,结果将包括连接表中没有任何匹配项的用户(没有任何角色的用户)。如果你想根据角色过滤用户,你需要使用.joins,它会创建一个 LEFT INNER JOIN 或在查询中添加一个having子句。
  • .where.includes 结合使用时实际发生的情况是,仅加载与谓词匹配的连接表行。
  • @max 所以我需要所有具有“管理员”角色的用户。或所有具有“管理员”或“作者”角色的用户。

标签: ruby-on-rails relationship has-many-through active-model-serializers


【解决方案1】:

由于 where 子句限制了加载的记录,您可能需要使用子查询:

User.includes(:roles).where(
  id: User.joins(:roles).where(roles: { name: params[:roles] })
)

如果您查看生成的 SQL,您会发现此处的 where 子句仅适用于子查询 - 因此加载了所有关联的角色,而不仅仅是匹配 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]]

此外,如果您想使用已加载的记录,则需要使用 map 而不是 pluck,因为 pluck 设计会创建一个单独的选择查询。

def roles
  object.roles.map(&:name)
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2018-08-21
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    相关资源
    最近更新 更多