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
вариант. Но в этом случае это не принесет вам большой пользы: объекты, которые вы сериализуете, должны, по крайней мере, реализовывать базовые методы для сериализуемой модели.
Поэтому вам нужно переписать ваш запрос, чтобы он возвращал коллекцию или массив записей.
увидеть: