Как написать сериализатор ActiveModel для отношений «многие ко многим»?

Попытка настроить серверную часть для приложения ember-cli. Вот как модели выглядят в Ember:

post.js

export default DS.Model.extend({
  heading: DS.attr('string'),
  content: DS.attr(''),
  imageUrl: DS.attr('string'),
  pageId: DS.belongsTo('page'),
  tagIds: DS.hasMany('tag')
});

tag.js

export default DS.Model.extend({
  name: DS.attr('string'),
  postIds: DS.hasMany('post')
});

Модели в Rails и Active Record — это просто Post, Tag и Theme. Тема объединяет Post и Tag. (т.е. опубликовать has_many :tags, через: :themes)

Вот как выглядят мои сериализаторы:

class PostSerializer < ActiveModel::Serializer
  embed :ids, include: true

  attributes :id, :heading, :content, :image_url

  has_many :tags
end

class TagSerializer < ActiveModel::Serializer
  embed :ids, include: true

  attributes :id, :name
end

Это работает в одном направлении: поисковые сообщения также получат все теги. В другом не работает, потому что у меня нет has_many в TagSerializer. Однако, если я поставлю has_many в оба сериализатора, будет слишком глубокая ошибка уровня стека.

Итак, я думаю, мой вопрос: каков типичный способ реализации отношений «многие ко многим» с сериализатором ActiveModel? Кажется, я не могу найти никаких ресурсов о том, как настроить это в серверной части Rails. Любая помощь будет оценена по достоинству. Спасибо!


person supahken    schedule 14.09.2014    source источник


Ответы (1)


Вы получаете ошибку «Слишком глубокий уровень стека», потому что каждый сериализатор рекурсивно встраивает другой.

Я бы начал с того, что убедился, что вы используете includes в своем контроллере Rails:

# posts controller
def show
  post = Post.includes(:tags).find_by id: params[:id]
  render json: post
end

# tags controller
def show
  tag = Tag.includes(:posts).find_by id: params[:id]
  render json: post
end

Затем в вашем сериализаторе скажите ему условно включать теги/сообщения, только если ассоциация была загружена:

# post serializer
def include_tags?
  object.association(:tags).loaded?
end

# tag serializer
def include_posts?
  object.association(:posts).loaded?
end

После этого он должен каскадироваться только на один уровень.

В качестве примечания, вы, вероятно, захотите переименовать свойства tagIds и postIds в ваших моделях Ember на tags и posts.

person Chris Ball    schedule 15.09.2014
comment
Спасибо! tagIds и postIds, по-видимому, используются по умолчанию при использовании адаптера ActiveModel. - person supahken; 15.09.2014
comment
Привет, Крис, не мог бы ты подробнее объяснить второй фрагмент кода? Я не уверен, как использовать include_tags? и include_posts? часть. - person supahken; 15.09.2014
comment
Конечно. ActiveModelSerializers автоматически вызывает include_<thing>? (если он определен), чтобы определить, должен ли json для атрибута или ассоциации быть в окончательном выводе. Также полезно условно отображать атрибуты, например, если текущий пользователь является владельцем или администратором. - person Chris Ball; 15.09.2014
comment
Хм, я только что попробовал это и посмотрел сообщение по id. Ответ XHR дал мне один пост с тегами в виде пустого массива. Выглядит нормально. - person supahken; 16.09.2014