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.jsaddHasMany()方法处理。

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 数据保存关系的主要内容,如果未能解决你的问题,请参考以下文章

Ember:关系链接相关数据未加载/消失

Ember:访问模板中的侧载模型关系数据

如何使嵌入的 hasMany 关系与 ember 数据一起使用

Ember 2 简单的多态关系

不要使用Ember.js从服务器获取新数据

Ember 数据:为啥 hasMany 和 belongsTo