Ember Data. 保存具有“belongsTo”关系的模型

3

我现在正在学习Ember Data,并遇到了一个问题,如果不写丑陋的代码就无法解决(这是我的项目,所以我有时间尽我所能)。问题是:

我有三个模型:Post、User和Comment。代码可以更好地描述我的意思;)

/* Post Model */

    Blog.Post = DS.Model.extend({
      title: DS.attr('string'),
      text: DS.attr('string'),
      postedAt: DS.attr('string', { defaultValue: new Date() }),
      comments: DS.hasMany("comment", { async: true })
    });

/* ==Post Model== */

/* User Model */

    Blog.User = DS.Model.extend({
      name: DS.attr('string'),
      avatar: DS.attr('string'),
      comments: DS.hasMany("comment", { async: true })
    });

/* ==User Model== */

/* Comment Model */

    Blog.Comment = DS.Model.extend({
        title: DS.attr("string"),
        text: DS.attr("string"),
        user: DS.belongsTo("user", { async: true }),
        post: DS.belongsTo("post", { async: true }),
        postedAt: DS.attr("date", { defaultValue: new Date() })
    });

/* ==Comment Model== */

/* Post Controller (bad variant and it doesn't work - too many redirects error) */

    leaveComment: function () {
        var controller = this, 
            user = controller.get('user'), 
            post = controller.get('model');
        var comment = this.get('store').createRecord('comment', {
              title: controller.get('commentTitle'),
              text:  controller.get('commentText'),
              user:  user,
              post:  post
            });
        comment.save().then(function (res) {
                controller.setProperties({
                    commentTitle: "",
                    commentText : ""
                });
                user.get("comments").then(function (comments) {
                    comments.pushObject(comment);
                    user.save().then(function (ures) {
                        console.log(ures);
                        post.get("comments").then(function (comments) {
                            comments.pushObject(comment);
                            post.save().then(function (pres) {
                                console.log(pres)
                            }, function (err) {
                                console.log(err);
                            });
                        });
                    }, function (err) {
                        console.log(err);
                    })
                });



            }, function (err) {
                console.log(err);
            });

/* ==Post Controller== */

/* Post Route */

Blog.PostRoute = Ember.Route.extend({
    setupController: function (controller, model) {
        this._super(controller, model);
        controller.setProperties({
            user: this.modelFor('application')
        });
    },
    model: function(params) {
        return this.store.find('post', params["post_id"]);
    }
});

/* ==Post Route== */

/* 2nd variant */

   /* Post Controller */
        leaveComment: function () {
            var controller = this, 
            user = controller.get('user'), 
            post =   controller.get('model');
            var comment = this.get('store').createRecord('comment', {
                title: controller.get('commentTitle'),
                text:  controller.get('commentText'),
                user:  user,
                post:  post
            });
            comment.save().then(function (res) {}, function(err) { console.log(err) });

    /* ==Post Controller== */

   /* Application.js */

      Blog.CommentSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
        primaryKey: function() {
            return '_id';
        }.property(),
        attrs: {
            user: { embedded: 'always' },//{serialize: 'id', deserialize: 'id'},
            post: { embedded: 'always' }//{serialize: 'id', deserialize: 'id'}
        }
      });

   /* ==Application.js== */

使用第二种方式进行保存时,我的预览数据中有用户/帖子ID而不是对象 - 可能我不知道如何正确使用它。如果您有类似的经验,请帮助我解决这个问题。
更新:
当我使用第一种方法发出请求时 - ERR:网络错误 - 重定向太多
当我使用第二种方法发出请求时:
1)似乎DS.ActiveModelSerializer根本不起作用 2)在请求中有user_id和post_id,而不是嵌入式对象,对我来说实际上并不意味着发送嵌入式对象或id,我关心的只是更新相关模型。 请查看屏幕截图以更好地了解。 Request using 2nd variant 当然,在进行此类保存后,它不包括在post.get("comments")和user.get("comments")属性中保存评论。
注:我也在Google上搜索了很长时间,没有得到我的问题的答案,所以如果您不确定您的答案,请不要回答 - 谢谢理解。
经过数小时的苦痛后,我想到了这个。
//Controller
comments: function () {
    return this.get('model.comments')
}.property('model.@each.comments'),
post: function () {
    return this.get('model');
}.property('post'),
user: function () {
    return this.get('user');
}.property('user'),
actions: {
  leaveComment: function () {
        var controller = this, 
        user = controller.get('user'), 
        post = controller.get('post');
        var comment = this.get('store').createRecord('comment', {
            text: controller.get('commentText'),
            user: user,
            post: post,
            postedAt: new Date()
        });
        comment.save().then(function (comment_res) {
            controller.get("comments").pushObject(comment_res);
            post.save().then(function (post_res) {
                controller.set("commentText", "");
            }, function (err) {
                console.log(err);
            });
        }, function (err) {
            console.log(err);
        });
    }
 }

    Blog.CommentSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
      primaryKey: function () {
        return '_id';
      }.property()//,
      //attrs: {//{ embedded: 'always' },
      //  user: { embedded: 'always' }, //{serialize: 'id', deserialize: 'id'},
      //  post: { embedded: 'always' } //{serialize: 'id', deserialize: 'id'}
      //} // <-this doesn't work :\ 
    });

    Blog.PostSerializer = DS.ActiveModelSerializer.extend({
      primaryKey: function () {
        return '_id';
      }.property(),
      serializeHasMany: function(record, json, relationship) {
        var key = relationship.key;
        var json_key = key.singularize().decamelize() + '_ids';

        var relationshipType = DS.RelationshipChange.determineRelationshipType(
          record.constructor, relationship);

        if (relationshipType === 'manyToNone' 
          || relationshipType === 'manyToMany' 
          ||  relationshipType === 'manyToOne') {
            json[json_key] = Ember.get(record, key).mapBy('id');
        }
     }// <- And this really works!

正如您所看到的,我只更新了一个相关的模型 - Post,由于出现了“Too-many redirections”错误,我无法更新User模型(我在Post和Comment之间有双向链接,在Comment中有与User的单向链接(Comment属于User),我无法获取具有User属性的评论)。目前为止这是不可能的。此外,我对DS.EmbeddedRecordMixin感到非常失望:( 我不会关闭这个话题直到找到我的问题的答案,谢谢你的帮助!


1
对于嵌入对象,请使用DS.EmbeddedRecordsMixin。我认为通常使用默认的Ember Data行为更可取,考虑仅使用ID更新您的模型,如果不起作用,则使用DS.EmbeddedRecordsMixin - Pooyan Khosravi
自从我上次评论以来,我一直在做Ember的工作,所以想用这些新知识再次评论。你说“使用第二种变体时,在执行保存操作时,预览数据中有用户/帖子ID而不是对象”-这是正确的。hasMany和belongsTo被保存为另一个模型上的ID。评论有一个用户ID,这就是它们与用户相关联的方式。等等。您不会将评论保存到用户中,您只是创建了三倍的开销,保存评论,将评论保存到用户和帖子。相反,您可以通过ID链接它。您的屏幕截图显示了应该的方式。 - Craicerjack
"它不包括将评论保存在post.get("comments")和user.get("comments")属性中。" - 这可能涉及到很多方面,从promises到序列化器,再到服务器返回数据的方式,以及数组是否是动态或静态的(自动更新或是静态)等。你的第一个模型看起来是正确的,但请在comment.save()行之前停止尝试,使用post.pushObject(comment)来查看是否更新了post.get('comments')数组。 - Craicerjack
请看下面我的回答更新。问题:如果您刷新页面,user.get('comments')post.get('comments')是否会更新? - Craicerjack
双向关系同步是Ember Data的一个问题,这个GitHub问题是一个很好的起点,但我不确定当前的状态。 - snovity
显示剩余3条评论
2个回答

2

@Craicerjack,

嗯,也许有点令人困惑 - 我解释这些东西的方式可能有点奇怪 :)

我试着让它更容易理解: 我们有3个模型:帖子(Post)用户(User)评论(Comment)帖子(Post)有许多评论(Comment)用户(User)有许多评论(Comment),并且评论(Comment)属于帖子(Post)评论(Comment)也属于用户(User)。这就是模型配置的描述,足够简单但非常重要,需要清楚地理解。

现在,难(对我来说)的部分:我们要发布评论!创建记录,comment.save()会向服务器发送ajax请求(post类型)。我们的评论对象包含评论文本(commentText)创建时间(createdAt)以及相对模型的“链接/引用/关系”- post_iduser_id -

comment.save().then(function(responseFromSavedComment) { 
    // we will insert next steps here... 
}); 

没问题。现在我们需要更新PostUser模型;我们刚刚添加了Comment,记得吗?好的!首先我们将更新Post;我们需要提取post.get('comments'),它返回Promise,然后将一个对象推送到其响应中,对吧?

post.get('comments').then(function(postComments) {
    postComments.pushObject(responseFromSavedComment);
}); 

我们在发布评论后,在承诺解析的范围内将最近创建的评论推送到帖子中。清楚吗?完美!
我们完成帖子更新的最后一步是通知服务器,我们只需要执行 post.save() 就好了! :) 就这样!
但是,如果我们想以同样的方式更新用户模型,我们会遇到 :TOO_MANY_REDIRECTS: 错误,就这样... 我们不能在添加评论的过程中完成这个操作...
2015年1月1日更新
我找到了解决方法 - 不使用嵌入式记录 :) 服务器会获取密钥并完成所有操作。目前无法完全在客户端处理此问题。

0
/* Post Controller  */

leaveComment: function () {
    var user = this.get('user');
    var post = this.get('post');
    var comment = this.store.createRecord('comment', {
        title: this.get('commentTitle'),
        text:  this.get('commentText'),
        user:  user,
        post:  post
    });
    this.get('model').pushObject(comment);
    comment.save();
    };

/* ==Post Controller== */

谢谢您的回答,但是我对这种方法有疑问:1)您建议将评论模型保存两次(可能是打错了),这会导致“太多重定向”错误;2)通常情况下,comment.set('user', user)并没有更新相关模型。在保存评论模型之后,您还应该保存与其关系的模型 - 用户和帖子模型。 - Blackening
它不会改变任何东西! :) 您将保存与用户和帖子相关的评论,但是用户和帖子并没有关于评论的信息! - Blackening
一旦您保存了评论,它就会有一个关联,并且应该可以通过该关联访问。var user_id = this.get('user').id; var userComments = this.get('comments').filterBy('user', user_id); - Craicerjack
你具体想做什么?这个问题的解决方案似乎应该是相对简单的。 - Craicerjack
你是否已经将所有需要的模型都准备好了?这些模型之间存在关联,但是你仍然需要在控制器中将它们准备好以便访问。这里有一篇帖子介绍如何在一个路由中加载多个模型,使用的例子与你的类似,是用户/帖子/评论的例子。 https://dev59.com/wGQn5IYBdhLWcg3w7KvS - Craicerjack
显示剩余6条评论

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