Sails.js:模型本身的一对多关系

时间:2014-12-26 22:43:41

标签: javascript angularjs node.js mongodb sails.js

我正在尝试使用Sails.js和MongoDB构建一个支持多级别类别的简单应用程序。类别可以有许多子类别。所以在我的Category模型中,我在其内部设置了一对多关系类别

module.exports = {

  adapter: 'mongo',

  attributes: {
    categoryTitle: {
      type: 'string',
      required: true
    },

//owner or parent of the one-to-many relationship
parentCat: {
  model: 'category'
},

//sub categories
subCategories: {
  collection: 'category',
  via: 'parentCat'
},
  }};

使用AngularJS中的ng-repeat指令在选择下拉列表中显示类别,例如

<select class="selector3" id="categoryParent" ng-model="category.parent">
                <option>Choose a category or none</option>
                <option ng-repeat="cat in categories" value="{{cat.categoryTitle}}">{{cat.categoryTitle}}</option>
            </select>

AngularJS CategoryController 中,我使用$scope.category.parent来捕获所选类别的值(作为父类别)。这一步有效,因为我能够通过console.log($scope.category.parent);看到所选的值。

但是,当我将新类别及其父类别一起保存到MongoDB时,即使正确保存其他字段,父类别也为空,如Mongo终端所示。 我想问题出在&#39;创建&#39;我写的用于在Sails中保存新类别的API 。您能否在以下代码中找出可能出错的地方?

create: function(req, res, next) {
var params = req.allParams();

// set parent category if exists
if (params.parentCat) {

    var parentCategory = Category.findOne({categoryTitle : params.parentCat})
        .exec(function(err, category) {
        if (err) {
            return null; //not found
        }

        return category; //found, return the category
    });

    params.parentCat = parentCategory;  //set the parent category in params
}

//insert the new category to MongoDB
Category.create(params, function(err, category) {
    if (err) {
        console.log('category addition failed');
        return next(err);
    }
    console.log('successfully added the category: ' + category.categoryTitle);
    res.redirect('/category');
});

} //create

1 个答案:

答案 0 :(得分:3)

你在这里混淆了一些概念。

首先

var parentCategory = Category.findOne({categoryTitle : params.parentCat})

请注意,水线方法findOne不会从数据库返回文档。因此,上述声明不会为您实现您的目的。重要的是你在exec函数中提供的回调函数。可以这样想:

Node.js事件循环将只执行Category.findOne并继续前进到下一个语句,而无需等待findOne的完成。这里的下一个陈述是:

params.parentCat = parentCategory

请注意,从MongoDB获取尚未完成。即使它已经完成,findOne也不会像我上面所说的那样返回对数据库中文档的引用。所以,这不是在Node.js中完成任务的方式。相反,如果要访问数据库对象,它将出现在exec中回调函数的category变量中。

现在第二个问题是下面的陈述

return category;
如你所知,

返回将完全让你退出创建行动。此外,这里需要注意的一点是,此语句与底部的重定向语句之间存在竞争。这就是为什么

Node.js事件循环执行它遇到的每个语句。如果该语句是阻塞语句(如var x = <some computation without callback function>),那么它将保留在该语句中并等待其完成。否则,如果语句是异步的,具有回调函数(如Category.findOne({}).exec(function (err, category){})),则事件循环将移至下一个语句,并且当findOne完成时将执行回调。

现在,在这种情况下,将执行findOne并将事件循环移至

params.parentCat = parentCategory; 

这不是异步语句,但它立即完成,事件循环转移到

Category.create()

现在,您可以将其视为findOne和create将在mongoDB中并行执行。完成数据库操作后,将执行回调函数。无论哪个回调首先执行,它的return语句都将被执行。所以,这种行为是不可预测的。 return category可以执行,也可以res.redirect('/category');执行。

我在下面重写了创建函数,保留了上面提到的所有要点

create: function(req, res, next) {
    var params = req.params.all();

    // set parent category if exists
    if (params.parentCat) {
        Category.findOne({categoryTitle : params.parentCat}).exec(function (err, parentCategory) {
            if (err) {
                return res.json(500, 'Sever error'); //not found
            }
            else{
                params.parentCat = parentCategory.id;  //set the parent category in params
                //insert the new category to MongoDB
                Category.create(params, function (err, category) {
                    if (err) {
                        console.log('category addition failed');
                        return next(err);
                    }
                    console.log('successfully added the category: ' + category.categoryTitle);
                    res.redirect('/category');
                });                     
            }
        });
    }
    else{
        return res.json(500,'Server error. Parent category required'); 
    }
}