【问题标题】:How to organize a node app that uses sequelize?如何组织使用 sequelize 的节点应用程序?
【发布时间】:2012-09-11 07:57:09
【问题描述】:

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

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

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

【问题讨论】:

标签: node.js express sequelize.js


【解决方案1】:

示例模型续集

'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;
};

【讨论】:

    【解决方案2】:

    您还可以使用依赖注入,它为此提供了一个优雅的解决方案。这是一个https://github.com/justmoon/reduct

    【讨论】:

      【解决方案3】:

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

      此外,就其界面而言,它的功能也更像 Mongoose。它允许您指定模型所在的一组位置,还允许您定义自定义匹配函数来匹配您的模型文件。

      用法基本是这样的:

      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;
      

      希望这对某人有所帮助。

      【讨论】:

      • 链接到文章有点帮助。引用一些文档更好。显示代码 sn-p 很棒....但实际上构建一个解决问题的库并将其放在 NPM 上是太棒了,值得更多的爱! +1 并将为您的项目加注星标。
      【解决方案4】:

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

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

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

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

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

      【讨论】:

        【解决方案5】:

        短篇小说

        在这种情况下,诀窍不是在文件中初始化模型,而只是为其初始化提供必要的信息,并让一个集中的模块负责模型的设置和实例化。 p>

        所以步骤是:

        • 拥有多个模型文件,其中包含有关模型的数据,例如字段、关系和选项。
        • 有一个单例模块,用于加载所有这些文件并设置所有模型类和关系。
        • 在 app.js 文件中设置您的单例模块。
        • 从单例模块获取模型类不要在您的模型文件中使用require,而是从单例加载模型。

        更长的故事

        这里是这个解决方案的更详细的描述以及相应的源代码:

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

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

        它在很多方面都过时且受到限制!

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

        • 第二 - 你在关系上非常受限 - 代码没有为这些关联提供选项,所以你是UNABLE 创建belongsToMany 因为它需要through 属性。你可以做最基本的联谊会。

        • 第三 - 你的模特关系非常有限!如果你仔细阅读代码,你会发现关系是一个Object而不是一个Array,所以如果你想多一个相同类型的关联(比如有两次belongsTo)- 你不能!

        • Fourth - 你不需要那个单身的东西。 nodejs 中的每个模块本身都是单例的,所以这一切都变得非常复杂。

        您应该会看到 Farm 的回答! (文章的链接已损坏,但我将使用 sequelize 的官方示例修复它:https://github.com/sequelize/express-example/blob/master/models/index.js - 您可以浏览整个项目以了解发生了什么)。

        附言 我正在编辑这篇文章,因为它的投票率很高,以至于人们甚至都看不到任何新答案(就像我一样)。

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

        【讨论】:

        • 另外,我的印象是节点中的所有required 模块在某种意义上都是单例的,因为它们中的代码执行一次然后缓存,所以下次你需要它们时获取缓存对象引用。这不是全部吗?
        • @mkoryak,你是对的 - node 中的所有 commonjs 模块实际上都是单例,因为返回的值在第一次执行后被缓存。 nodejs.org/api/modules.html#modules_caching
        • 因此,可以通过删除单例棘手的部分来简化示例,只需放入 module.exports = new OrmClass()。我会试试看,谢谢你的反馈:)
        • 以防万一有人让我头疼,我会救你的。我对以路径为中心的 github 文章中列出的代码有疑问。我不得不添加一个 .到要求(像这样: var object = require('.' + modelsPath + "/" + name);) 并在 init 函数的 forEach 中放入 return if name.indexOf('DS_Store') > -1 (耶 OSX)。希望对您有所帮助。
        • 正如@jinglesthula 提到的 - 示例中有一些更改/错误用于加载目录中的文件(特别是如果它嵌套在其他地方)。我还将添加将选项传递给关系的功能,因为它们非常重要(例如外键的名称,是否允许为空等)
        【解决方案6】:

        SequelizeJS has a article 在他们的网站上解决了这个问题。

        链接已损坏,但您可以找到工作示例项目here 并浏览它。请参阅上面的编辑答案,了解为什么这是一个更好的解决方案。

        文章摘录:

        • 模型/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)
          

        【讨论】:

        • 这是 Sequelize 推荐的方式。我会接受这是正确的答案。
        • 这很好,但你不能使用来自另一个模型的实例方法的模型,或者我错过了一些东西。
        • 页面不存在了
        • @mlkmt 你可以!由于您可以访问模型文件中的sequelize 变量,因此您可以使用sequelize.models.modelName 访问您的其他模型。
        【解决方案7】:

        我将其设置为农场和文档描述。

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

        通过让所有模型都可以访问它们来解决这个问题。

        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
         });
        };
        

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

        【讨论】:

        • +1 用于返回数据库的原型类方法。正是我一直在寻找的想法,即能够在定义期间加载 classMethods,但也能够在 ClassMethod 中引用任何模型(即包含关系)
        【解决方案8】:

        我按照官方指南:http://sequelizejs.com/heroku,里面有一个models文件夹,把每个模块设置在单独的文件中,并有一个索引文件来导入它们并设置它们之间的关系。

        【讨论】:

        • 链接无效
        【解决方案9】:

        您可以使用sequelize.import 从其他文件导入模型 http://sequelizejs.com/documentation#models-import

        这样您就可以拥有一个用于 sequelize 的单例模块,然后加载所有其他模型。

        其实这个答案和user1778770的答案很相似。

        【讨论】:

        • 这对循环依赖有用吗?例如,当模型 A 对模型 B 具有 FK 并且模型 be 对模型 A 具有 FK 时
        【解决方案10】:

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

        因此,我不再使用 Sequelize。此外,我正在从模型中使用从数据库中获取的任何数据切换。在我看来,最好完全抽象获取数据。原因是 - 假设您不只是使用 MySQL(在我的情况下,我同时使用 MySQL 和 MongoDB),但您可以从任何数据提供者和任何传输方法获取数据,例如SQL、no-SQL、文件系统、外部 API、FTP、SSH 等。如果您尝试在模型中完成所有这些操作,您最终会创建复杂且难以理解的代码,难以升级和调试。

        现在您要做的是让模型从知道从何处以及如何获取数据的层获取数据,但您的模型仅使用 API 方法,例如fetchsavedelete 等。在这一层内,您有特定数据提供者的特定实现。例如。您可以从本地机器上的 PHP 文件或 Facebook API 或 Amazon AWS 或远程 HTML 文档等请求某些数据。

        PS其中一些想法是 Cloud9Architect 那里借来的:http://events.yandex.ru/talks/300/

        【讨论】:

        • 这些是有效的点,但我宁愿避免在Sequelize 之外重新实现fetchsavedelete 等,因为框架已经提供了方法。有一个单独的获取层更好,但不太方便。同时,您可能可以在 Sequelize 周围添加一个提取抽象层,但解决方案会更加复杂,这是一个值得商榷的胜利。
        • 这个教程很有帮助:sequelize+express example
        • @mvbl-fst 你刚刚描述了一个 DAO 层。假设您在 SQL DB 中有一些用户,而在文件系统中有不同的用户。您应该有两个 DAO 来抽象如何获取它们中的每一个,然后是一个业务层,它将用户连接在一起(甚至可能调整一些属性)并将它们传递回您的路由(表示层)。
        猜你喜欢
        • 1970-01-01
        • 2011-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-12
        • 2017-12-03
        • 2019-11-04
        • 2017-08-20
        相关资源
        最近更新 更多