active_model_serializer возвращает более одного результата

Я пытаюсь вернуть another_id для связанной записи. Я бы просто добавил отношение has_many и own_to для каждого проекта, но мне нужно иметь идентификатор пользователя, чтобы получить правильные результаты. Однако, с кодом, который я имею ниже, он возвращает все возможные another_ids для current_user.

Если я введу это в 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 сериализует больше чем одну запись.

Я использую рельсы 4.2.3 и active_model_serializers 0.8.3. Боюсь, я не могу изменить схему. Кроме того, это, вероятно, не имеет значения, но это API для приложения Ember.

Заранее спасибо. Я немного смущен, что у меня проблемы с этим.

Редактировать:

Наверное, стоит упомянуть, что так выглядит модель моего проекта:

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 действительно возвращает правильный идентификатор.

Также я знаю, что у меня неправильно. Дело в том, что я не могу просто использовать ассоциации activerecord, потому что это зависит от текущего пользователя этого сеанса.

Изменить 2:

Я думал, что смогу заставить это работать, если я только рендерил это, используя: render json: Project.all.to_jsonи избавился от another_id__sql метод в сериализаторе. Это работает, если у него есть another_id, Однако, если это ноль, я получаю ошибку: "NoMethodError в 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 theкамень active_model_serializers`. Я был неправ! Смотрите мой ответ ниже.

3 ответа

Вся логика БД принадлежит вашей модели, а не вашему сериализатору. Сериализаторы просто указывают, что должно быть открыто, но не должны отвечать за его вычисление. Так вот, я бы посоветовал сделать это another_id метод в вашей модели, который не решит вашу проблему (так как кажется, что это проблема SQL больше, чем что-либо еще), но сделает так, чтобы у вас больше не было проблем с AMS.

Понял! Похоже, мне нужен был еще один метод в сериализаторе:

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 это одна запись проекта.

Я все еще хотел бы услышать лучшие способы сделать это!

Сериализаторы берут запись и возвращают сериализованное представление, подходящее для кодирования JSON или XML.

Они предназначены в качестве альтернативы засорению ваших контроллеров этим:

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

И ментальный метеоризм, который является jbuilder.

Запросы и области SQL вместо этого принадлежат вашим моделям.

Вы можете установить сериализатор, используя each_serializer вариант. Но в этом случае это не принесет вам большой пользы: объекты, которые вы сериализуете, должны, по крайней мере, реализовывать базовые методы для сериализуемой модели.

Поэтому вам нужно переписать ваш запрос, чтобы он возвращал коллекцию или массив записей.

увидеть:

Другие вопросы по тегам