【问题标题】:active_model_serializer returning more than one resultactive_model_serializers 返回多个结果
【发布时间】:2015-10-20 18:45:54
【问题描述】:

我正在尝试返回相关记录的 another_id。我只想为每个项目添加一个 has_many 和 belongs_to 关系,但我需要有用户 ID 才能返回正确的结果。但是,使用下面的代码,它会返回 current_user 的所有可能的 another_ids。

如果我将它输入到 psql 中,它可以正常工作:

WITH RECURSIVE t(id, parent_id, path) AS (
    SELECT thing.id, thing.parent_id, ARRAY[thing.id]
    FROM thing, projects
    WHERE thing.id = 595
  UNION
    SELECT i.id, i.parent_id, i.parent_id || t.path
    FROM thing i
    INNER JOIN t ON t.parent_id = i.id
)
SELECT DISTINCT user_thing.another_id FROM user_thing
INNER JOIN t on t.id = user_thing.thing_id
WHERE t.id = user_thing.thing_id AND user_thing.user_id = 2;

 another_id 
-----------
         52
(1 row)

但如果我从序列化程序运行代码,它会返回:[52, 51]:

class ProjectSerializer < ActiveModel::Serializer
  attributes :id, :another_id

  def another_id__sql
    "(WITH RECURSIVE t(id, parent_id, path) AS (
        SELECT thing.id, thing.parent_id, ARRAY[thing.id]
        FROM thing, projects
        WHERE thing.id = projects.thing_id
      UNION
        SELECT i.id, i.parent_id, i.parent_id || t.path
        FROM thing i
        INNER JOIN t ON t.parent_id = i.id
    )
    SELECT DISTINCT user_thing.another_id FROM user_thing
    INNER JOIN t on t.id = user_thing.thing_id
    WHERE t.id = user_thing.thing_id AND user_thing.user_id = #{options[:current_user].id})"
  end
end

class API::V1::ProjectsController < API::APIController
  def index
    render json: Project.all
  end

  private

  def default_serializer_options
    { current_user: @current_user }
  end
end

据我所知,我不了解 active_model_serializers 如何序列化多个记录。

我正在使用 rails 4.2.3 和 active_model_serializers 0.8.3。恐怕我无法更改架构。此外,这可能无关紧要,但这是 Ember 应用程序的 API。

提前致谢。我有点尴尬,我在这方面遇到了麻烦。

编辑:

我应该提一下,这就是我的项目模型的样子:

class Project < ActiveRecord::Base
  belongs_to :thing
  has_many :user_thing, through: :thing

  attr_accessor :another_id

  def set_another_id(user)
    connection = ActiveRecord::Base.connection
    result = connection.execute("(WITH RECURSIVE t(id, parent_id, path) AS (
        SELECT thing.id, thing.parent_id, ARRAY[thing.id]
        FROM thing, projects
        WHERE thing.id = #{thing_id}
      UNION
        SELECT i.id, i.parent_id, i.parent_id || t.path
        FROM thing i
        INNER JOIN t ON t.parent_id = i.id
    )
    SELECT DISTINCT user_thing.another_id FROM user_thing
    INNER JOIN t on t.id = user_thing.thing_id
    WHERE t.id = user_thing.thing_id AND user_thing.user_id = #{user.id})")

    @another_id = result[0]["another_id"].to_i
  end
end

这是控制器中的显示动作:

def show
  @project = Project.find(params[:id])
  @project.set_another_id(@current_user)
  render json: @project
end

show 操作确实返回了正确的 id。

另外,我知道我所拥有的是不正确的。问题是我不能只使用 activerecord 关联,因为它取决于该会话的当前用户。

编辑 2:

我认为如果我只是使用 render json: Project.all.to_json 渲染它,我就能让它工作,并摆脱了序列化程序中的 another_id__sql 方法。如果它确实有another_id,那确实有效。但是,如果那是 nil,我会收到错误消息:“API::V1::ProjectsController#index 未定义方法 []' for nil:NilClass". It looks like this is a possible bug in 0.8, so I'll either have to ask another Stack Overflow question, or I'll have to see if I can upgrade theactive_model_serializers` gem 中的 NoMethodError。 我错了!请参阅下面的答案。

【问题讨论】:

  • 你说它没有返回正确的ID。它究竟返回了什么?错误记录的 ID?
  • 更多问题:如果您在用户 ID 中硬编码,它是否返回正确的 ID? SQL(Rails 之外)是否始终如一地为不同的记录返回正确的 ID,或者它是否可能实际上无法正常工作但碰巧为该特定记录返回了正确的 ID?我的一个想法是result[0]["another_id"] 实际上可能返回 nil,而 #to_i 方法会将其转换为 0。结果是 0 吗?
  • 它返回一个包含该模型所有 id 的数组。比如有两个anothers:51和52。它应该返回52,但它实际上返回[51, 52]。我知道这是因为我拥有的 SQL 正在寻找所有项目的所有适用记录,但我不知道如何解决。如果我对 thing_id 进行硬编码,SQL 确实可以工作。

标签: ruby-on-rails active-model-serializers


【解决方案1】:

所有数据库逻辑都属于您的模型,而不是您的序列化程序。序列化程序只是说明应该公开的内容,但它不应该负责计算它。 所以在这里,我建议在你的模型上使用这个 another_id 一个方法,它不会解决你的问题(因为它看起来更像是一个 SQL 问题而不是其他任何问题),但它会让你AMS 不再有问题。

【讨论】:

  • 问题是我在模型中找不到another_id。这取决于当前用户的user_id。我可以使用has_many :through 找到所有another 关联,但它会返回所有记录。
  • 好吧,使用您在更新中提供的代码,another_id 是在序列化程序启动时设置的,因此它应该直接通过执行 belongs_to :another 来工作。
  • 我没有belongs_to 方法。我正在使用版本 0.8.3,它没有。另外,如果我尝试使用has_one,它会显示“nil:NilClass 的未定义方法‘选项’”。
  • 您的another_id 的实际名称是什么?
  • 哦等等,我知道那是错误的。您应该在模型上设置 another 方法,以便它返回实际对象。 AMS 目前的工作方式,如果你包含它,它会获取资源,然后使用another.id
【解决方案2】:

序列化器获取一条记录并返回适合 JSON 或 XML 编码的序列化表示。

它们的目的是替代在你的控制器上乱扔垃圾:

render json: @users, except: [:foo, :bar, :baz], include: [..........]

还有jbuilder的精神胀气。

SQL 查询和作用域属于您的模型。

您可以使用each_serializer 选项设置序列化程序。但在这种情况下,您序列化的对象必须至少实现the base methods for a serializable model,这对您没有多大好处。

因此,您需要重新编写查询,使其返回记录集合或数组。

见:

【讨论】:

    【解决方案3】:

    知道了!看来我需要在序列化程序中再添加一种方法:

    project_serializer.rb

    def another_id
      object.another_id
    end
    
    def another_id__sql
      # sql from before
    end
    

    我不能 100% 确定为什么会这样,但我注意到,如果我省略了 another_id__sql,我会收到错误 column.projects.another_id does not exist。所以,我猜another_id__sql 在返回数组时会被调用,但在object 是单个项目记录时使用another_id 方法。

    我仍然希望听到更好的方法来做到这一点!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多