【问题标题】:Ember data saving a relationshipEmber 数据保存关系
【发布时间】: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,这并不是我真正想要的想。

我想这是一个多对多的关系,我不确定它是否得到支持,但是关于如何实现我所描述的任何想法?谢谢

【问题讨论】:

    标签: javascript ember.js ember-data


    【解决方案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 ...
          }
        })
      })
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多