sequelizeを使用するノードアプリを整理する方法は?


125

sequelize ORMを使用するnodejsアプリの例を探しています。

require()依存ループのためにモデルが互いに複雑な関係を持っている場合、モデルを別々のjsファイルで定義することはほとんど不可能に思われるのが私の主な懸念です。多分人々はすべてのモデルを非常に長い1つのファイルで定義しますか?

私は主に、モデルがどのように定義され、アプリ全体で使用されるかに興味があります。私が自分でやっていることは、物事を行うための「良い」方法であることを検証したいのですが。



:私は、私たちのソリューションについての記事書かれているmedium.com/@ismayilkhayredinov/...
hypeJunction

回答:


125

短編小説

この場合のトリックは、モデルを初期化することはないファイルが、ちょうどその初期化のためのnecesary情報を提供し、モデルのセットアップおよびインスタンス化の集中管理モジュールの世話をしてみましょう。

したがって、手順は次のとおりです。

  • フィールド、関係、オプションなど、モデルに関するデータを含むいくつかのモデルファイルを用意します。
  • これらのファイルをすべてロードし、すべてのモデルクラスと関係を設定するシングルトンモジュールを用意します。
  • app.jsファイルでシングルトンモジュールを設定します。
  • モデルファイルで使用しないシングルトンモジュールからモデルクラスを取得し、require代わりにシングルトンからモデルをロードします。

長い話

このソリューションの詳細な説明と、対応するソースコードを次に示します。

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

編集:これは非常に古い答えです!(情報を読む)

それは古く、多くの点で制限されています!

  • 最初に、@ jinglesthulaがコメントで述べたように(そして私も経験しました)、これらのファイルの要求に問題があります。とrequire同じように動作しないためreaddirSyncです!

  • 第二に -あなたがしている非常に関係が制限-コードが用意されていませんオプションあなたがいるので、これらの団体へのUNABLE作成するbelongsToManyことが必要とthrough財産を。最も基本的な関連を作成できます。

  • 第三に、あなたはモデルの関係が非常に限られています!コードをよく読むと、RelationsがArrayではなくObjectであることがわかります。したがって、同じタイプの関連付けを2つ以上作成したい場合(2回持つ場合など)はできません。belongsTo

  • 第四 -あなたはそのシングルトンの物事を必要としません。nodejs内のすべてのモジュールはそれ自体がシングルトンであるため、これによりすべてが複雑になり、理由はまったくありません。

ファームの答えが表示されます。(記事へのリンクは壊れていますが、sequelizeの次の公式サンプルで修正します:https : //github.com/sequelize/express-example/blob/master/models/index.js-閲覧できます何が起こっているのかを知るためのプロジェクト全体)。

ps私はこの投稿を編集しているので、あまりにも賛成されているので、(私が行ったように)新しい回答は表示されません。

編集:リンクを同じ投稿のコピーに変更しましたが、Githubページにあります


また、requirenode内のすべてのdモジュールは、コードが1回実行されてからキャッシュされるため、ある意味でシングルトンであり、次回それらを必要とするときに、キャッシュされたオブジェクト参照を取得するという印象を受けました。これは全体像ではありませんか?
mkoryak

1
@mkoryak、そのとおりです。戻り値は最初の実行後にキャッシュされるため、nodeのすべてのcommonjsモジュールは事実上シングルトンです。 nodejs.org/api/modules.html#modules_caching
Casey Flynn

2
したがって、シングルトンのトリッキーな部分を削除してmodule.exports = new OrmClass()を配置するだけで、例を簡略化できます。私はそれを試してみます、あなたのフィードバックに感謝します:)
user1778770

2
誰かが私の頭痛を抱えている場合に備えて、私はあなたを救います。パスを中心としたgithubの記事に記載されているコードに問題がありました。追加する必要がありました。requireに(このように:var object = require( '。' + modelsPath + "/" + name);)、さらにinit関数のforEachにname.indexOf( 'DS_Store')> -1の場合に戻り値を入れます(そう、OSX)。お役に立てば幸いです。
jinglesthula 2013

@jinglesthulaが言及したように-ディレクトリにファイルをロードするためのサンプルにいくつかの変更/バグがあります(特に他の場所にネストされている場合)。オプションが非常に重要であるため、リレーションにオプションを渡す機能も追加します(外部キーの名前、nullを許可されている場合など)
Andrey Popov

96

SequelizeJSのウェブサイトに、この問題を解決する記事があります

リンクは壊れていますが、ここで動作するサンプルプロジェクトを見つけて参照できます。これがより良いソリューションである理由を確認するには、上記の編集された回答を参照してください。

記事からの抜粋:

  • models / 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
このページはもう存在しません
Mike Cheel

1
これが実際のリンクです:sequelize.readthedocs.org/en/1.7.0/articles/express
chrisg86

3
@mlkmtできます!sequelizeモデルファイルの変数にアクセスできるため、を使用して他のモデルにアクセスできますsequelize.models.modelName
Guilherme Sehn

29

この問題への対処を支援するために、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;

うまくいけば、これは誰かを助けます。


3
記事へのリンクは少し役立ちます。いくつかのドキュメントを引用する方が良いです。コードスニペットを表示するのは素晴らしいことです。しかし、実際に問題を解決してNPMに配置するライブラリを構築することは素晴らしいことであり、愛に値するものです。+1すると、プロジェクトにスターが付けられます。
Stijn de Witt 2016

9

Express.jsアプリでSequelizeを使い始めました。すぐに、あなたが説明している自然の問題に遭遇しました。たぶん私はSequelizeを完全に理解していなかったかもしれませんが、1つのテーブルから選択すること以上のことをすることは私にとって本当に便利ではありませんでした。通常、2つ以上のテーブルからの選択、またはピュアSQLのユニオンを使用する場合、個別のクエリを実行する必要があり、ノードの非同期の性質により、複雑さが増します。

そのため、Sequelizeの使用をやめました。さらに、モデルでDBからフェッチするすべてのデータの使用から切り替えています。私の意見では、取得するデータを完全に抽象化する方が良いです。そして理由は-MySQLを使用するだけではない(私の場合、MySQLとMongoDBを並べて使用する)と想像してください。ただし、SQL、no-SQLなどの任意のデータプロバイダーとトランスポートメソッドからデータを取得できます。ファイルシステム、外部API、FTP、SSHなど。すべてをモデルで実行しようとすると、最終的には複雑で理解しにくいコードが作成され、アップグレードやデバッグが難しくなります。

ここで、モデルがデータを取得する場所と方法を知っているレイヤーからデータを取得するようにしますが、モデルfetchsavedeleteなどのAPIメソッドのみを使用します。このレイヤー内には、特定のデータプロバイダーの特定の実装があります。たとえば、ローカルマシンのPHPファイル、Facebook API、Amazon AWS、リモートHTMLドキュメントなどから特定のデータをリクエストできます。

PSこれらのアイデアのいくつかは、Cloud9によってArchitectから借用されましたhttp : //events.yandex.ru/talks/300/


これらは有効なポイントですが、私はむしろ再実装避けるだろうfetchsavedeleteなどの外部のSequelizeフレームワークはすでにの手段を提供することを与えられました。別のフェッチングレイヤーを使用する方が便利ですが、あまり便利ではありません。同時に、おそらくSequelizeの周囲にフェッチする抽象化レイヤーを追加することもできますが、その場合、解決策はより複雑になり、議論の余地があるでしょう。
Zorayr 2016

このチュートリアルは非常に役立ちます:sequelize + expressの例
Lucas Do Amaral

@ mvbl-fst DAOレイヤーについて説明しました。SQL DBに何人かのユーザーがいて、ファイルシステムに別のユーザーがいるとします。それぞれを取得する方法を抽象化する2つのDAOが必要です。次に、ユーザーを連結し(場合によってはいくつかのプロパティを調整し)、それらをルート(プレゼンテーションレイヤー)に返すビジネスレイヤーが必要です。
DJDaveMark 2018年

5

私はそれをファームとしてセットアップし、ドキュメントで説明しています。

しかし、各関数のモデルにアタッチするインスタンスメソッドとクラスメソッドで、他のデータベースオブジェクトを保持するためにインデックスファイルが必要になるという追加の問題がありました。

すべてのモデルがアクセスできるようにすることで解決しました。

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

これはクラスメソッドに対してのみ行いましたが、インスタンスメソッドに対しても同じことができます。


データベースを返すプロトタイプclassMethodの+1。定義中にclassMethodsをロードできるようにするだけでなく、ClassMethodで任意のモデルを参照できる(つまり、関係を含めるため)ために探していたアイデア
bitwit

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

http://sequelizejs.com/documentation#models-importを使用して、他のファイルからモデルをインポートできますsequelize.import

そうすることで、sequelizeのシングルトンモジュールを1つ持つことができ、それが他のすべてのモデルをロードします。

実際、この回答はuser1778770の回答と非常によく似ています。


1
これは循環依存関係で機能しますか?たとえば、モデルAにモデルBへのFKがあり、モデルbeにモデルAへのFKがある場合
mkoryak

1

sequelize ORMを使用するnodejsアプリの例を探しています。

PEAN.JSのボイラープレートソリューションをご覧ください。

PEAN.JSは、フルスタックのJavaScriptオープンソースソリューションであり、PostgreSQL、Node.js、Express、およびAngularJSベースのアプリケーションの確実な出発点を提供します。

PEANプロジェクトはMEAN.JSプロジェクトのフォークです(MEAN.IOまたは汎用 MEANスタックと混同しないでください)。

PEANは、MongoDBとMongoose ORMをPostgreSQLとSequelizeに置き換えます。MEAN.JSプロジェクトの主な利点は、多くの動く部分があるスタックに提供する組織です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.