Правильный метод для определения магистрально-реляционных отношений на основе ключей

Я несколько раз просматривал документы, но этот аспект до сих пор мне не ясен. Вполне возможно, что я думаю, что backbone-relational делает что-то, чего на самом деле нет.

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

Возьмем канонический пример «Исполнители и альбомы»: у исполнителя много альбомов, как определено в альбоме.artist_id.

/api/artist/62351 может вернуться

{
  id: 62351,
  name:"Jimi Hendrix"
}

аналогично /api/album?artist_id=62351 может вернуться

[
 {
   id:5678,
   name: "Are You Experienced?"
   artist_id: 62351
 },
 {
   id: 4321,
   name: "Axis: Bold as love",
   artist_id: 62351
 }
]

Как я могу определить отношения «Исполнитель» и «Альбом» так, чтобы

var hendrixInstance = new Artist({id:62351});
hendrixInstance.get('albums');

будет извлекать и возвращать коллекцию альбомов на основе альбома foreign_key artist_id? Это должно быть просто какая-то перестановка key/keySource/keyDestination, которую я еще не пробовал, или это проблема, которую магистрально-реляционная не пытается решить, но мой поиск документа не удался, и я думаю, что краткий ответ на это на SO может помочь будущим сотрудникам Google.

var Artist = Backbone.RelationalModel.extend({
  urlRoot: '/api/artist',
  relations:[{
    key: 'albums', //Docs say this is the foreign key name, but in practice it doesn't appear that way. Need keySource/Destination?
    type: Backbone.HasMany,
    reverseRelation: {
      key: 'artist',
      type: Backbone.HasOne
    }
  }]
});
var Album = Backbone.RelationalModel.extend({
  urlRoot: '/api/album'
});

Бонус указывает на пример модели, которая ссылается на свой стиль списка смежности с parent_id.


person nebulous    schedule 12.04.2013    source источник


Ответы (2)


Итак, описанный выше метод @xzhang заставлял меня повторять эту проблему. Во-первых, я хотел бы оказаться неправым в этом, но я не нашел способа, которым backbone-relational справляется с этой проблемой без дополнительного пользовательского кода. Поскольку это, на мой взгляд, невероятно простой пример отношений OneToMany, я все еще надеюсь, что просто не понимаю чего-то очевидного.

Вот что я сделал, чтобы справиться с ситуацией. К сожалению, он по-прежнему не загружается автоматически с сервера, когда вызывается someobject.fetch('somerelationship'), чего я действительно хочу. Функция синтаксического анализа не нужна большинству людей, но она необходима для API, который я вызываю.

Сначала я создал базовую коллекцию для расширения:

  var BaseCollection = Backbone.Collection.extend({
    initialize: function(models, options) {
      if (_.isObject(options.relation)) {
      this.url = '/api/'
        + options.relation.keySource
        + '?search.'+options.relation.reverseRelation.keySource
        + '=' + options.foreignId;
      }
    },
    parse: function(res) { return res.success ? res.list : res },
  });

Затем повторно используемая вспомогательная функция (вероятно, может быть включена в BaseCollection), чтобы помочь в создании отношений.

  function collectionOptions(instance) {
    return {"relation":this, "foreignId":instance.get('id') };
  };

И, наконец, этим отношениям предлагается использовать BaseCollection в качестве их CollectionType, а хелпер collectionOptions() назначается для установки collectionOptions.

 var Form = BaseModel.extend({
    urlRoot: '/api/form',
    relations:[
      {
        key: 'fills',
        keySource: 'fill',
        relatedModel: Fill,
        type: Backbone.HasMany,
        collectionOptions: collectionOptions,
        collectionType: BaseCollection,
        reverseRelation: {
          key: 'form',
          keySource: 'form_id',
          type: Backbone.HasOne
        }
      },{
        key: 'children',
        keySource: 'form',
        relatedModel: 'Form',
        type: Backbone.HasMany,
        collectionOptions: collectionOptions,
        collectionType: BaseCollection,
        reverseRelation: {
          key: 'parent',
          keySource: 'parent_id',
          type: Backbone.HasOne
        }
      }
    ]
  });

Это позволяет мне избежать изменения API на стороне сервера, чтобы он возвращал список идентификаторов, а затем извлекал эти идентификаторы по отдельности. Вместо этого я могу просто:

var form = new Form({id:1});
form.get('children').fetch();
form.toJSON(); //now has {id:1, ..., ..., children:[child,child,child,...]}

Расширение дочерних элементов autoFetch при первом вызове .get('children') было бы просто билетом, но я не нашел, как это сделать без изменения самого backbone-relational.

person nebulous    schedule 16.04.2013

Я столкнулся именно с проблемой (Backbone-relational-hasmany-best-practices">Backbone-relational имеет много лучших практик), через 2 дня исследуйте и изучайте исходный код, я не думаю, что key/keySource/keyDestination сделает работу (поправьте меня, если я ошибаюсь).

Итак, я создаю свой собственный тип отношения, пока работает нормально. Это может быть не очень хорошим решением, но надежда может вам помочь.

var LazyMany = Backbone.HasMany.extend({
  setRelated: function (related) {
    var relation = this.options
      , instance = this.instance
    ;

    if (related && !_.result(related, 'url')) {
      related.url = relation.relatedModel.prototype.urlRoot + 
        '?' + relation.reverseRelation.key + '=' + instance.id;
    }
    return LazyMany.__super__.setRelated.apply(this, arguments);
  }
});

Затем в вашей модели:

var Album = Backbone.RelationalModel.extend({
  urlRoot: '/api/album/'
});

var Artist = Backbone.RelationalModel.extend({
  urlRoot: '/api/artist/',
  relations:[{
    key: 'albums',
    type: LazyMany,
    includeInJSON: false,
    relatedModel: Album,
    reverseRelation: {
      key: 'artist',
      // I didn't test this, I don't have artist_id, artist is "id" in my app
  keySource: 'artist_id',
  keyDestination: 'artist_id',
      includeInJSON: 'id'
    }
  }]
});

Поэтому, если вы не определяете тип коллекции или в вашей коллекции нет поля URL, LazyMany создаст коллекцию с URL-адресом: /api/album/?artist=62351. Тогда вам просто нужно получить коллекцию: artist.get('albums').fetch().

Надеюсь, это может помочь, и я все еще ищу лучшие решения.

person xzhang    schedule 12.04.2013
comment
Спасибо @xzhang, я рад видеть, что я не единственный, кто ожидал чего-то другого от магистральной связи. Я попробовал ваш метод, и он действительно работает, но после нескольких дополнительных итераций я использовал вариант. - person nebulous; 16.04.2013