如何组织使用续集的节点应用程序?


125

我正在寻找一个使用序列化ORM的示例nodejs应用程序。

我主要担心的是,如果由于require()依赖关系循环而导致模型之间具有复杂的关系,那么几乎不可能在单独的js文件中定义模型。也许人们在一个非常长的文件中定义了所有模型?

我主要对模型的定义和在整个应用程序中的使用感兴趣。我想验证一下我自己正在做的事情是“好的”做事方式。


2
我添加了一个示例,可能会帮助某人github.com/shaishab/sequelize-express-example
Shaishab Roy

我写了一篇有关我们解决方案的文章:medium.com/@ismayilkhayredinov/…–
hypeJunction

Answers:


125

短篇小说

这种情况下的窍门不是初始化文件中的模型而只是提供初始化所需的信息,并让集中的模块负责模型的设置和实例化。

因此,步骤如下:

  • 有几个Model文件,其中包含有关模型的数据,例如字段,关系和选项。
  • 有一个单例模块,该模块可以加载所有这些文件并设置所有模型类和关系。
  • 在app.js文件中设置您的单例模块。
  • 获得从单模块模型类使用require来自单上的模型文件,加载型号代替使用。

更长的故事

这是带有相应源代码的此解决方案的更详细描述:

http://jeydotc.github.io/blog/2012/10/30/EXPRESS-WITH-SEQUELIZE.html

编辑:这是一个非常古老的答案!(请阅读以获取信息)

它在许多方面都古老而有限!

  • 首先,正如@jinglesthula在评论中提到的(我也经历过)-要求这些文件存在问题。这是因为requirereaddirSync!的工作方式不同!

  • 其次 -您的关系非常有限-该代码没有为这些关联提供选项,因此您无法belongsToMany根据需要创建through属性。您可以进行最基本的关联。

  • 第三 -您在模型关系方面非常有限!如果仔细阅读代码,您会发现关系是一个Object而不是Array,因此,如果您要建立多个相同类型的关联(例如两次belongsTo),则不能!

  • 第四 -您不需要那个单身的东西。nodejs中的每个模块本身都是单例的,因此所有这些使它毫无原因地变得非常复杂。

您应该看到Farm的答案!(本文的链接已断开,但我将使用sequelize中的此官方示例对其进行修复:https : //github.com/sequelize/express-example/blob/master/models/index.js-您可以浏览整个项目以了解发生了什么)。

ps:我正在编辑这篇文章,因为它被推高了,人们甚至都看不到任何新答案(就像我一样)。

编辑:只是将链接更改为同一文章的副本,但在Github页面中


同样,我的印象是require节点中的所有d模块在某种意义上都是单例的,因为它们中的代码执行一次然后被缓存,因此下次需要它们时,您将获得一个缓存对象引用。这不是全部吗?
mkoryak

1
@mkoryak,您是对的-节点中的所有commonjs模块实际上都是单例,因为返回的值在第一次执行后就被缓存了。 nodejs.org/api/modules.html#modules_caching
Casey Flynn

2
因此,可以通过删除单例棘手的部分并仅放置module.exports = new OrmClass()来简化示例。我会尝试一下,谢谢您的反馈:)
user1778770 2013年

2
万一有人头疼我,我会救你的。我在github文章中列出的围绕路径的代码有问题。我必须添加一个。到require(例如:var object = require('。'+ modelsPath +“ /” + name);),并且如果init函数中的name.indexOf('DS_Store')> -1放在forEach中,还返回(是OSX)。希望有帮助。
jinglesthula

正如@jinglesthula所提到的-示例中存在一些更改/错误,用于通过目录加载文件(尤其是嵌套在其他地方)。我还将添加将选项传递给关系的功能,因为它们非常重要(例如外键的名称,如果允许为空,等等)
Andrey Popov 2015年

96

SequelizeJS在其网站上有一篇文章可以解决此问题。

链接已断开,但是您可以在此处找到可用的示例项目并进行浏览。请参阅上面的编辑答案,以了解为什么这是一个更好的解决方案。

文章摘录:

  • 模型/ index.js

    该文件的目的是配置与数据库的连接并收集所有模型定义。一切就绪后,我们将调用每个模型上关联的方法。此方法可用于将模型与其他模型关联。

          var fs        = require('fs')
            , path      = require('path')
            , Sequelize = require('sequelize')
            , lodash    = require('lodash')
            , sequelize = new Sequelize('sequelize_test', 'root', null)
            , db        = {} 
    
          fs.readdirSync(__dirname)
            .filter(function(file) {
              return (file.indexOf('.') !== 0) && (file !== 'index.js')
            })
            .forEach(function(file) {
              var model = sequelize.import(path.join(__dirname, file))
              db[model.name] = model
            })
    
          Object.keys(db).forEach(function(modelName) {
            if (db[modelName].options.hasOwnProperty('associate')) {
              db[modelName].options.associate(db)
            }
          })
    
          module.exports = lodash.extend({
            sequelize: sequelize,
            Sequelize: Sequelize
          }, db)

12
这是Sequelize建议这样做的方式。我会接受这是正确的答案。
jpotts18 2013年

3
很好,但是您不能使用另一个模型的实例方法中的模型,或者我错过了一些东西。
mlkmt 2015年

1
该页面不存在了
麦克Cheel


3
@mlkmt,你可以的!由于您可以访问sequelize模型文件中的变量,因此可以使用来访问其他模型sequelize.models.modelName
Guilherme Sehn

29

我创建了一个包sequelize-connect来帮助人们解决这个问题。它遵循此处的Sequelize建议约定:http ://sequelize.readthedocs.org/en/1.7.0/articles/express/

此外,就界面而言,它的功能也更像猫鼬。它允许您指定模型所在的位置集,还可以定义自定义匹配函数以匹配模型文件。

用法基本上是这样的:

var orm = require('sequelize-connect');

orm.discover = ["/my/model/path/1", "/path/to/models/2"];      // 1 to n paths can be specified here
orm.connect(db, user, passwd, options);                        // initialize the sequelize connection and models

然后,您可以访问模型并像这样进行续集:

var orm       = require('sequelize-connect');
var sequelize = orm.sequelize;
var Sequelize = orm.Sequelize;
var models    = orm.models;
var User      = models.User;

希望这可以帮助某人。


3
链接到文章会有所帮助。引用一些文档更好。显示代码片段很棒。。。。。。。但是,实际上,建立一个解决问题的库并将其放在NPM上确实很棒,值得更多的爱!+1并将为您的项目加注星标。
Stijn de Witt

9

我开始在Express.js应用程序中使用Sequelize。很快就遇到了您所描述的性质的问题。也许我不太了解Sequelize,但是对我来说,做事情不仅仅是从一张桌子中进行选择并不十分方便。通常,在两个或多个表中使用select或在纯SQL中使用并集的情况下,您将不得不运行单独的查询,并且由于Node的异步特性,这只会增加复杂性。

因此,我不再使用Sequelize。此外,我从使用模型中的DB提取任何数据切换。我认为最好完全抽象地获取数据。原因是-想象您不只是使用MySQL(在我的情况下,我并排使用MySQL和MongoDB),而是可以从任何数据提供程序和任何传输方法(例如SQL,no-SQL,文件系统,外部API,FTP,SSH等。如果您试图在模型中完成所有这些操作,最终将创建复杂且难以理解的代码,而这些代码将难以升级和调试。

现在,你想要做的是有机型从知道在哪里以及如何获得它一个层中获取数据,但您的模型只使用API的方法,例如fetchsavedelete等这层内你有具体的数据提供商的具体实现。例如,您可以从本地计算机上的PHP文件或Facebook API或Amazon AWS或远程HTML文档等中请求某些数据。

PS一些想法是从借来的设计师通过CLOUD9http://events.yandex.ru/talks/300/


这些都是正确的观点,但我宁愿避免重新实现fetchsavedelete等之外Sequelize鉴于框架已经提供了手段。更好,但是不方便使用单独的获取层。同时,您可能会在Sequelize周围添加一个提取抽象层,但这样的解决方案变得更加复杂,这是有争议的。
Zorayr

本教程非常有帮助:续集和表达示例
Lucas Do Amaral

@ mvbl-fst您刚刚描述了DAO层。假设您在SQL DB中有一些用户,在文件系统上有不同的用户。您应该有两个DAO,分别抽象出如何获取每个DAO,然后是一个将用户连接在一起的业务层(甚至可以修改某些属性),然后将它们传递回您的路线(表示层)。
DJDaveMark

5

我将其设置为Farm并在文档中进行了描述。

但是我遇到了一个附加的问题,即在我要附加到每个函数中的模型的实例方法和类方法中,我将需要索引文件来保存其他数据库对象。

通过使所有模型都可以访问它们来解决此问题。

var Config = require('../config/config');

 var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var _ = require('lodash');
var sequelize;
var db = {};

var dbName, dbUsername, dbPassword, dbPort, dbHost;
// set above vars

var sequelize = new Sequelize(dbName, dbUsername, dbPassword, {
dialect: 'postgres', protocol: 'postgres', port: dbPort, logging: false, host: dbHost,
  define: {
    classMethods: {
        db: function () {
                    return db;
        },
        Sequelize: function () {
                    return Sequelize;
        }

    }
  }
});


fs.readdirSync(__dirname).filter(function(file) {
   return (file.indexOf('.') !== 0) && (file !== 'index.js');
}).forEach(function(file) {
  var model = sequelize.import(path.join(__dirname, file));
  db[model.name] = model;
});

Object.keys(db).forEach(function(modelName) {
  if ('associate' in db[modelName]) {
      db[modelName].associate(db);
  }
});

module.exports = _.extend({
  sequelize: sequelize,
  Sequelize: Sequelize
}, db);

并在模型文件中

var classMethods = {
  createFromParams: function (userParams) {
    var user = this.build(userParams);

    return this.db().PromoCode.find({where: {name: user.promoCode}}).then(function (code) {
        user.credits += code.credits;
                return user.save();
    });
  }

};

module.exports = function(sequelize, DataTypes) {
  return sequelize.define("User", {
  userId: DataTypes.STRING,
}, {  tableName: 'users',
    classMethods: classMethods
 });
};

我只对类方法这样做,但是您也可以对实例方法做同样的事情。


对于返回db的原型classMethod +1。正是我一直希望能够在定义期间加载classMethod,但又能够引用ClassMethod中的任何Model(即包含关系)的
想法

2

我正在遵循官方指南:http : //sequelizejs.com/heroku,它具有一个models文件夹,将每个模块设置在单独的文件中,并具有一个索引文件以导入它们并设置它们之间的关系。


链接无效
prisar

2

样本模型序列化

'use strict';
const getRole   = require('../helpers/getRole')
const library   = require('../helpers/library')
const Op        = require('sequelize').Op

module.exports = (sequelize, DataTypes) => {
  var User = sequelize.define('User', {
    AdminId: DataTypes.INTEGER,
    name: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          args: true,
          msg: 'Name must be filled !!'
        },
      }
    },
    email: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          args: true,
          msg: 'Email must be filled !!'
        },
        isUnique: function(value, next) {
          User.findAll({
            where:{
              email: value,
              id: { [Op.ne]: this.id, }
            }
          })
          .then(function(user) {
            if (user.length == 0) {
              next()
            } else {
              next('Email already used !!')
            }
          })
          .catch(function(err) {
            next(err)
          })
        }
      }
    },
    password: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          args: true,
          msg: 'Password must be filled !!'
        },
        len: {
          args: [6, 255],
          msg: 'Password at least 6 characters !!'
        }
      }
    },
    role: {
      type: DataTypes.INTEGER,
      validate: {
        customValidation: function(value, next) {
          if (value == '') {
            next('Please choose a role !!')
          } else {
            next()
          }
        }
      }
    },
    gender: {
      type: DataTypes.INTEGER,
      validate: {
        notEmpty: {
          args: true,
          msg: 'Gender must be filled !!'
        },
      }
    },
    handphone: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          args: true,
          msg: 'Mobile no. must be filled !!'
        },
      }
    },
    address: DataTypes.TEXT,
    photo: DataTypes.STRING,
    reset_token: DataTypes.STRING,
    reset_expired: DataTypes.DATE,
    status: DataTypes.INTEGER
  }, {
    hooks: {
      beforeCreate: (user, options) => {
        user.password = library.encrypt(user.password)
      },
      beforeUpdate: (user, options) => {
        user.password = library.encrypt(user.password)
      }
    }
  });

  User.prototype.check_password = function (userPassword, callback) {
    if (library.comparePassword(userPassword, this.password)) {
      callback(true)
    }else{
      callback(false)
    }
  }

  User.prototype.getRole = function() {
    return getRole(this.role)
  }

  User.associate = function(models) {
    User.hasMany(models.Request)
  }

  return User;
};




1

我正在寻找一个使用序列化ORM的示例nodejs应用程序。

您可能对查看PEAN.JS样板解决方案感兴趣。

PEAN.JS是一个全栈JavaScript开源解决方案,为基于PostgreSQL,Node.js,Express和AngularJS的应用程序提供了坚实的起点。

PEAN项目是MEAN.JS项目的分支(不要与MEAN.IO或通用 MEAN堆栈混淆)。

PEAN用PostgreSQL和Sequelize代替了MongoDB和Mongoose ORM。MEAN.JS项目的主要好处是它为具有许多可移动组件的堆栈提供了组织。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.