Ember数据保存关联关系

9

我需要帮助翻译一篇关于IT技术的文章,涉及到ember data中的一对多关系保存问题。以下是需要翻译的内容:

在ember data中,我遇到了保存一对多关系的困难。我的关系如下:

App.ParameterSet = DS.Model
    name: DS.attr("string")
    regions: DS.hasMany("App.Region")

App.Region = DS.Model
    name: DS.attr("string")

如果我要做这样的事情:
parameterSet = App.ParameterSet.find(5)
@transaction = @get("store").transaction()
@transaction.add(parameterSet)
region1 = App.Region.find(10)
region2 = App.Region.find(11)
parameterSet.set("name", "foo")
parameterSet.get("regions").pushObject(region)
@transaction.commit()

我希望您能够发送一个如下所示的带有负载的PUT请求:

然后我想看到一个带有这样负载的PUT请求:

api/ParameterSets/5

{parameterSet: {name: "foo", regionIds:[10, 11]}}

但我得到的是这个:

{parameterSet: {name: "foo"}}

我不关心子级到父级的关系,但是如果我将parameterSet: DS.belongsTo("App.ParameterSet")添加到App.Region模型中,则会向regions url发送两个PUT请求,用于这两个新关系,这并不是我想要的。

我猜这实际上是一个多对多的关系,但我不确定是否已经支持,您有什么想法可以实现我所描述的吗?谢谢

2个回答

8
hasMany关系的序列化由json_serializer.js中的addHasMany()方法处理。
以下注释包含在源代码中:

默认的REST语义是仅在嵌入式时添加has-many关系。如果关系最初通过ID加载,我们假设这样做是为了性能优化,并且对has-many进行的更改应该保存为子级belongs-to关系上的外键更改。

要实现您想要的效果,一个选项是在适配器中指示关系应嵌入。
App.Store = DS.Store.extend({
  adapter: DS.RESTAdapter.extend({
    serializer: DS.RESTSerializer.extend({
      init: function() {
        this._super();
        this.map('App.ParameterSet', {
          regions: { embedded: 'always' }
        });
      }
    })
  })
});

当然,现在你的后端需要将相关地区的JSON嵌入到参数集的JSON中。如果你想保持现状,可以使用自定义序列化来覆盖addHasMany()

App.Store = DS.Store.extend({
  adapter: DS.RESTAdapter.extend({
    serializer: DS.RESTSerializer.extend({
      addHasMany: function(hash, record, key, relationship) {
        // custom ...
      }
    })
  })
});

这非常有帮助,谢谢!我认为这是 Ember-data 做出的一个非常短视的假设! - KOGI
这个“优化”的原因是什么?我可以看出它在加载期间是一种优化,但在保存期间它如何帮助呢?如果我想要向一个父对象添加100个子对象,那么这种方式就需要将父ID添加到每个子对象中,并单独保存每个子对象(100个单独的POST/PUT请求),而我本来可以只将所有100个ID添加到父对象中,然后进行一次POST/PUT请求即可完成。 - KOGI
这是一个很好的问题。最初,addHasMany 只是一个占位符,期望开发人员根据需要实现它;只有后来才指定了默认行为。这确立了一种约定,但我猜测假设仍然是开发人员应根据需要自定义序列化。 - ahmacleod
1
值得一看,关于dirtyRecordsForHasManyChange,请参考下面meelash的帖子。 - ahmacleod

2
我无法在ahmacleod的答案中添加评论,但它是正确的,除了我注意到当子记录被修改时,父记录没有被标记为脏数据。在问题的示例中,这个问题并不会出现,因为父记录上的名称也被修改了。
总的来说,如果你要遵循ahmacleod的第二个答案,你需要覆盖RESTAdapter中的dirtyRecordsForHasManyChange方法。否则,在序列化器中的addHasMany永远不会被调用,因为记录甚至没有被标记为脏数据。
现有的方法看起来像:
dirtyRecordsForHasManyChange: function(dirtySet, record, relationship) {
  var embeddedType = get(this, 'serializer').embeddedType(record.constructor, relationship.secondRecordName);

  if (embeddedType === 'always') {
    relationship.childReference.parent = relationship.parentReference;
    this._dirtyTree(dirtySet, record);
  }
},

我猜你需要类似这样的东西:
```html

所以我猜你想要这样的东西:

```
App.Store = DS.Store.extend({
  adapter: DS.RESTAdapter.extend({
    dirtyRecordsForHasManyChange: function(dirtySet, record, relationship) {
      relationship.childReference.parent = relationship.parentReference;
      this._dirtyTree(dirtySet, record);
    },

    serializer: DS.RESTSerializer.extend({
      addHasMany: function(hash, record, key, relationship) {
        // custom ...
      }
    })
  })
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接