active_model_serializer返回多个结果

时间:2015-10-20 18:45:54

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

我试图将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

这是控制器中的show动作:

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,那确实有效。但是,如果没有,我得到错误:&#34; API :: V1中的NoMethodError :: ProjectsController #index undefined method []' 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 the active_model_serializers` gem。我错了!请参阅下面的答案。

3 个答案:

答案 0 :(得分:1)

所有数据库逻辑都属于您的模型,而不是您的序列化程序。序列化器只是说明应该暴露什么,但它不应该负责计算它。 所以在这里,我建议在您的模型上使用another_id方法,这不会解决您的问题(因为它似乎更像是一个SQL问题而不是其他任何东西),但它会使它如此你不再有AMS的问题了。

答案 1 :(得分:0)

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

它们可以替代乱丢你的控制器:

$('#link-title').on('click', function(){
    window.open('https://linkaddress.com','_blank');
    window.open('https://linkaddresstwo.com','_blank');
});

jbuilder的精神胀气。

SQL查询和范围改为属于您的模型。

您可以使用render json: @users, except: [:foo, :bar, :baz], include: [..........] 选项设置序列化程序。但是在这种情况下,序列化的对象必须至少实现the base methods for a serializable model,这对你没有多大帮助。

因此,您需要重新编写查询,以便返回一个集合或记录数组。

见:

答案 2 :(得分:0)

知道了!看来我在序列化器中还需要一个方法:

<强> 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在返回数组时被调用,但在another_id是单个项目记录时使用object方法。< / p>

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