web-dev-qa-db-fra.com

Comment organiser une application de noeud qui utilise Sequelize?

Je suis à la recherche d'un exemple d'application Nodejs utilisant l'ORM séquestre.

Ma principale préoccupation est qu'il semble presque impossible de définir vos modèles dans des fichiers js distincts si ces modèles ont des relations complexes les unes par rapport aux autres en raison de boucles de dépendance require (). Peut-être que les gens définissent tous leurs modèles dans un fichier qui est très très long?

Je suis principalement intéressé par la façon dont les modèles sont définis et utilisés dans l'application. J'aimerais avoir la confirmation que ce que je fais seul est la "bonne" façon de faire les choses.

112
mkoryak

La nouvelle

Dans ce cas, l’astuce n’est pas d’initialiser le modèle in du fichier, mais de fournir les informations nécessaires à son initialisation et de laisser un module centralisé se charger de la configuration et de l’instanciation des modèles.

Les étapes sont donc les suivantes:

  • Plusieurs fichiers de modèle contenant des données sur le modèle, tels que des champs, des relations et des options.
  • Ayez un module singleton qui charge tous ces fichiers et configurez toutes les classes et relations du modèle.
  • Configurez votre module singleton dans le fichier app.js.
  • Récupérez les classes de modèle à partir du module singleton ne pas utiliser require sur vos fichiers de modèle; chargez les modèles à partir du singleton à la place.

La longue histoire

Voici une description plus détaillée de cette solution avec le code source correspondant:

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

EDIT: C'est une très vieille réponse! (lire pour info)

C'est vieux et limité à bien des égards!

  • First, comme indiqué dans les commentaires de @jinglesthula (et je l'ai aussi expérimenté) - il est difficile d'exiger ces fichiers. C'est parce que require ne fonctionne pas de la même manière que readdirSync!

  • Deuxième - vous êtes très limité dans les relations - le code ne fournit pas options à ces associations, vous êtes donc INCAPABLE de créer belongsToMany car il a besoin de through propriété. Vous pouvez faire les assocs les plus basiques.

  • Troisième - vous êtes très limité dans les relations de modèle! Si vous lisez attentivement le code, vous verrez que relations est un Object au lieu d'un Array, donc si vous voulez faire plus d'une association du même type (comme si vous aviez deux fois belongsTo) - vous ne pouvez pas!

  • Quatrième - Vous n'avez pas besoin de ce singleton. Chaque module de nodejs est singleton par lui-même, donc tout ceci est complexe sans raison.

Vous devriez voir la réponse de Farm! (Le lien vers l'article est cassé, mais je vais le réparer avec cet échantillon officiel de sequelize: https://github.com/sequelize/express-example/blob/master/models/index.js - vous pouvez parcourir l’ensemble du projet pour avoir une idée de ce qui se passe).

p.s . J'édite ce billet car il est tellement voté que les gens ne verront même pas de nouvelles réponses (comme je l'ai fait).

Edit: vient de changer le lien en une copie du même message, mais dans une page Github

114
user1778770

SequelizeJS a un article sur son site internet qui résout ce problème. 

Le lien est rompu, mais vous pouvez trouver le projet exemple en cours ici et le parcourir. Voir la réponse modifiée ci-dessus pour voir pourquoi il s'agit d'une meilleure solution.

Extrait de l'article:

  • modèles/index.js

    L'idée de ce fichier est de configurer une connexion à la base de données et de collecter toutes les définitions de modèle. Une fois que tout est en place, nous appellerons la méthode associée à chacun des modèles. Cette méthode peut être utilisée pour associer le modèle à d’autres.

          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)
    
92
Farm

J'ai créé un package sequelize-connect pour aider les gens à faire face à ce problème. Voici la convention suggérée par Sequelize: http://sequelize.readthedocs.org/fr/1.7.0/articles/express/

De plus, il fonctionne un peu plus comme Mongoose en termes d’interface. Il vous permet de spécifier un ensemble d'emplacements où se trouvent vos modèles et vous permet également de définir une fonction de correspondance personnalisée correspondant à vos fichiers de modèle.

L'utilisation est fondamentalement comme ceci:

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

Ensuite, vous pouvez accéder aux modèles et vous séquencer comme suit:

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

Espérons que cela aide quelqu'un.

25
Jacob

J'ai commencé à utiliser Sequelize dans l'application Express.js. Bientôt, assez de problèmes rencontrés dans la nature que vous décrivez. Je ne comprenais peut-être pas vraiment Sequelize, mais pour moi, faire plus que simplement choisir une table ne me convenait pas vraiment. Et là où vous utiliseriez habituellement select parmi deux tables ou plus, ou une union en SQL pur, vous auriez à exécuter des requêtes distinctes, et avec la nature asynchrone de Node, cela ne fait qu'ajouter à la complexité. 

Par conséquent, je me suis écarté de l'utilisation de Sequelize. De plus, je remplace l'utilisation de TOUT type de récupération de données à partir de la base de données dans les modèles. À mon avis, il est préférable d’abstraire complètement l’obtention de données. Et les raisons en sont: imaginez que vous n'utilisez pas simplement MySQL (dans mon cas, j'utilise MySQL et MongoDB côte à côte), mais vous pouvez obtenir vos données auprès de n'importe quel fournisseur de données et de toute méthode de transport, par exemple. SQL, no-SQL, système de fichiers, API externe, FTP, SSH, etc. Si vous essayez de tout faire dans les modèles, vous allez éventuellement créer du code complexe et difficile à comprendre qui sera difficile à mettre à niveau et à déboguer.

Désormais, vous voulez que les modèles obtiennent les données d'une couche qui sait où et comment les obtenir, mais vos modèles utilisent uniquement des méthodes API, par exemple. fetch, save, delete etc. Et à l'intérieur de cette couche, vous avez des implémentations spécifiques pour des fournisseurs de données spécifiques. Par exemple. vous pouvez demander certaines données à partir d'un fichier PHP sur une machine locale ou à partir de l'API Facebook, d'Amazon AWS ou d'un document HTML distant, etc.

PS certaines de ces idées ont été empruntées à Architecte par Cloud9: http://events.yandex.ru/talks/300/

9
mvbl fst

Je l'ai configuré comme ferme et la documentation décrit.

Mais j’avais le problème supplémentaire que j’attacherais dans mes méthodes d’instance et de classe aux modèles de chaque fonction le fichier d’index pour pouvoir accéder à d’autres objets de base de données.

Résolu en les rendant accessibles à tous les modèles.

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

Et dans le fichier modèle

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

Je l'ai fait uniquement pour les méthodes de classe, mais vous pouvez également faire la même chose pour les méthodes d'instance.

4
jacob

Je suis le guide officiel: http://sequelizejs.com/heroku , qui contient un dossier de modèles, configure chaque module dans des fichiers distincts et dispose d’un fichier d’index pour les importer et définir la relation entre eux. 

2
Ron

Exemple de modèle sequelize

'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

Je suis à la recherche d'un exemple d'application Nodejs utilisant l'ORM séquestre.

Vous pourriez être intéressé par la solution standard de PEAN.JS.

PEAN.JS est une solution open source JavaScript à pile complète qui constitue un solide point de départ pour les applications basées sur PostgreSQL, Node.js, Express et AngularJS. 

Le projet PEAN est un fork du projet MEAN.JS (à ne pas confondre avec MEAN.IO ou la pile generic MEAN).

PEAN remplace MongoDB et Mongoose ORM par PostgreSQL et Sequelize. Le principal avantage du projet MEAN.JS réside dans l'organisation qu'il fournit à une pile comportant de nombreuses pièces en mouvement.

1
mg1075

Vous pouvez importer des modèles à partir d'autres fichiers avec sequelize.importhttp://sequelizejs.com/documentation#models-import

De cette façon, vous pouvez avoir un module singleton pour sequelize, qui charge ensuite tous les autres modèles.

En fait, cette réponse est assez similaire à la réponse de user1778770.

1
natrixnatrix89

Vous pouvez également utiliser une injection de dépendance qui fournit une solution élégante à cela. En voici un https://github.com/justmoon/reduct

0
Vahe Hovhannisyan