EmberData: две модели, связанные с отношениями hasMany

У меня есть логика приложения, которая требует, чтобы две модели имели взаимные отношения hasMany. В качестве примера представьте набор задач GitHub, которые можно пометить несколькими ярлыками.

Я пытаюсь использовать адаптер, расширяющий стандартный RESTAdapter. Все приложение работает нормально, но двойное отношение hasMany вызывает исключение. Копаясь в коде, метод inverseBelongsToForHasMany выдает исключение.

Итак, я предполагаю, что Ember.Data не поддерживает ассоциацию двух моделей с отношениями hasMany с обеих сторон, и для каждого hasMany требуется связанный с ним компонент containsTo. Мои вопросы:

  1. Поддерживается ли это, и проблема в том, что я делаю что-то не так?
  2. Если это не поддерживается, планируется ли появление этой функции?
  3. Следует ли избегать этого типа ассоциации в приложениях такого типа? Если да, то какой лучший подход или обходной путь?

заранее спасибо


person escalant3    schedule 08.11.2012    source источник


Ответы (2)


Мы используем аналогичный метод создания объекта ассоциации. Однако вместо того, чтобы переопределять имеющиеся методы, мы просто добавили объекты соединения в API.

поэтому в моделях мы создаем:

App.Hashtag = DS.Model.extend({
  hashtagUsers: DS.hasMany('App.HashtagUser', {key: 'hashtag_user_ids'})   
});

App.User = DS.Model.extend({
  hashtagUsers: DS.hasMany('App.HashtagUser', {key: 'hashtag_user_ids'})
});

App.HashtagUser = DS.Model.extend({
  user: DS.belongsTo('App.User'),
  hashtag: DS.belongsTo('App.Hashtag')
});

Затем для транзакций мы просто изменяем и фиксируем объект соединения.

App.UserController = Ember.ObjectController.extend({
  followHashtag: function(tag) {
    var hashtagUser;
    hashtagUser = this.get('hashtagUsers').createRecord({
      hashtag: tag
    });
    tag.get('hashtagUsers').pushObject(hashtagUser);
    App.store.commit();
  }
  unfollowHashtag: function(tag) {
    var itemToRemove;
    itemToRemove = this.get('hashtagUsers').find(function(hashtagUser) {
      if (hashtagUser.get('hashtag') === this) {
        return true;
      }
    }, tag);
    this.get('hashtagUser').removeObject(itemToRemove);
    tag.get('hashtagUser').removeObject(itemToRemove);
    itemToRemove.deleteRecord();
    App.store.commit();   

});

API создает объект HashtagUser, а метод follow просто добавляет этого пользователя в обе связанные части.

Для удаления он извлекает связанные объекты и уничтожает объект ассоциации.

Хотя это не так элегантно, как могло бы быть, нашей главной мотивацией было то, что когда Ember Data обновится, мы сможем легче перевести его на простую стандартную версию с поддержкой Ember Data, чем если бы мы возились с самим Магазином.

person Andre Malan    schedule 09.11.2012
comment
В последних версиях Ember Data вы можете использовать App.ApplicationSerializer = DS.ActiveModelSerializer.extend({}); для использования внешних ключей с подчеркиванием. - person rxgx; 10.01.2014

Отношения «многие ко многим» еще не поддерживаются в ember-data. На данный момент одним из возможных обходных путей является ручное управление таблицей соединения.

A = DS.Model.extend({
  abs: DS.hasMany('Ab'),

  bs: function () {
    return this.get('abs').getEach('b'); 
  }
});

Ab = DS.Model.extend({
  a: DS.belongsTo('A'),
  b: DS.belongsTo('b')
});

B = DS.Model.extend({
  abs: DS.hasMany('Ab'),

  bs: function () {
    return this.get('abs').getEach('a'); 
  }
});

Это только отправная точка. Затем вам нужно настроить свои модели и адаптер, чтобы отправлять/получать/сохранять записи в рабочем режиме

Например, в нашем приложении мы вводим параметр { includedJoin: true } внутри отношений hasMany и объявляем таблицу соединений как JoinModel.

A = DS.Model.extend({
  abs: DS.hasMany('Ab', {includeJoin: true}),
  ...
});

DS.JoinModel = DS.Model.extend();

Ab = DS.JoinModel.extend({
  ... belongsTo relationships ...
});

Затем в адаптере мы переопределяем методы создания/обновления/удаления, чтобы игнорировать жизненный цикл таблицы соединений в хранилище.

createRecords: function (store, type, records) {
  if (!DS.JoinModel.detect(type)) {
    this._super(store, type, records);
  }
}

Наконец, в сериализаторе мы переопределяем функцию addHasMany, чтобы отправлять данные соединения на сервер в виде встроенных идентификаторов в родительских моделях.

addHasMany: function (hash, record, key, relationship) {
  var 
    options = relationship.options,
    children = [];

  //we only add join models, use of `includeJoin`
  if (options.includedJoin) {
    record.get(relationship.key).forEach(function (child) {
      children.pushObject(child.toJSON({
        includeId: true
      }));
    });
    hash[key] = children;
  }
}

На стороне сервера мы используем Rails с ActiveModelSerializer, поэтому единственная непростая настройка — это когда мы обновляем родительские модели, мы вручную управляем отношением соединений и создаем/удаляем записи в таблице соединений.

person sly7_7    schedule 09.11.2012
comment
Спасибо за ваш подробный ответ. Ручная обработка взаимосвязей была отправной точкой, которую мы знали, но ваш подробный анализ изменений, которые необходимо выполнить в адаптере/сериализаторе, поразителен. - person escalant3; 09.11.2012
comment
Приятно знать, что это помогает вам :). Это не идеально и явно взламывает, но, похоже, работает для нас. Надеюсь, у вас получится реализовать серверную работу с этим. - person sly7_7; 09.11.2012