Ember 数据保存关系
Posted
技术标签:
【中文标题】Ember 数据保存关系【英文标题】:Ember data saving a relationship 【发布时间】:2013-02-14 19:15:25 【问题描述】:我很难在 ember 数据中保存一对多关系。我有这样的关系:
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 请求,其负载如下:
api/ParameterSets/5
parameterSet: name: "foo", regionIds:[10, 11]
但是我得到了这个:
parameterSet: name: "foo"
我不关心从孩子到父母的关系,但如果我将 parameterSet: DS.belongsTo("App.ParameterSet")
添加到 App.Region 模型,那么我会收到 2 个 PUT 请求到两个新关系的区域 url,这并不是我真正想要的想。
我想这是一个多对多的关系,我不确定它是否得到支持,但是关于如何实现我所描述的任何想法?谢谢
【问题讨论】:
【参考方案1】:hasMany
关系的序列化由json_serializer.js
的addHasMany()
方法处理。
source code 中包含以下注释:
默认的 REST 语义是只添加一个 has-many 关系,如果它 是嵌入的。如果关系最初是由 ID 加载的,我们假设 这是作为性能优化完成的,并且更改为 has-many 应该保存为孩子的外键更改 关系。
要实现您想要的,一种选择是指示应将关系嵌入到您的适配器中。
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 来说,这是一个非常短视的假设! 这种“优化”的原因是什么?我可以看到它是加载期间的优化,但它在保存期间有什么帮助?如果我想将 100 个子对象添加到父对象,这样,我必须将父 ID 添加到每个子对象,并单独保存每个子对象(100 个单独的 POST/PUT 请求),而我可以 刚刚将所有 100 个 ID 添加到父级并完成了一次 POST/PUT 并已完成。 这是个好问题。最初,addHasMany
只是一个占位符,期望开发人员可以根据需要实现它;只是后来才指定默认行为。这建立了一个约定,但我猜测仍然是开发人员应该根据需要自定义序列化。
值得看看 meelash 在下面的帖子:dirtyRecordsForHasManyChange
。【参考方案2】:
我无法将 cmets 添加到 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);
,
所以我猜你会想要这样的东西:
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 ...
)
)
);
【讨论】:
以上是关于Ember 数据保存关系的主要内容,如果未能解决你的问题,请参考以下文章