Come organizzare un'app nodo che utilizza sequelize?


125

Sto cercando un'app nodejs di esempio che utilizza il sequelize ORM.

La mia preoccupazione principale è che sembra quasi impossibile definire i tuoi modelli in file js separati se quei modelli hanno relazioni complesse tra loro a causa dei cicli di dipendenza request (). Forse le persone definiscono tutti i loro modelli in un unico file molto lungo?

Sono principalmente interessato a come i modelli sono definiti e utilizzati attraverso l'app. Mi piacerebbe avere qualche conferma che quello che sto facendo da solo è il "buon" modo di fare le cose.


2
Ho aggiunto un esempio che potrebbe aiutare qualcuno github.com/shaishab/sequelize-express-example
Shaishab Roy,

Ho scritto un articolo sulla nostra soluzione: medium.com/@ismayilkhayredinov/…
hypeJunction

Risposte:


125

La breve storia

Il trucco in questo caso non è quello di inizializzare il modello nel file ma solo di fornire le informazioni necessarie per la sua inizializzazione e lasciare che un modulo centralizzato si occupi della configurazione e della creazione di istanze del modello.

Quindi i passaggi sono:

  • Disponi di diversi file di modello con dati sul modello, ad esempio campi, relazioni e opzioni.
  • Avere un modulo singleton che carica tutti quei file e imposta tutte le classi e le relazioni del modello.
  • Imposta il tuo modulo singleton nel file app.js.
  • Ottieni le classi del modello dal modulo singleton che non usi requiresui tuoi file modello, carica invece i modelli dal singleton.

La storia più lunga

Ecco una descrizione più dettagliata di questa soluzione con il codice sorgente corrispondente:

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

EDIT: questa è una risposta molto vecchia! (leggi per info)

È vecchio e limitato in molti modi!

  • Innanzitutto , come menzionato da @jinglesthula nei commenti (e anche io l'ho sperimentato) - ci sono problemi con la richiesta di quei file. È perché requirenon funziona allo stesso modo di readdirSync!

  • In secondo luogo - si sono molto limitati nelle relazioni - il codice non prevede opzioni a tali associazioni e pertanto si è IMPOSSIBILE creare belongsToManycome ha bisogno di throughproprietà. Puoi creare i soci più elementari.

  • Terzo : sei molto limitato nelle relazioni modello! Se leggi attentamente il codice, vedrai che le relazioni sono un oggetto anziché un array , quindi se vuoi creare più di una associazione dello stesso tipo (come avere due volte belongsTo), non puoi!

  • Quarto : non hai bisogno di quel singleton. Ogni modulo in nodejs è singleton da solo, quindi tutto ciò rende abbastanza complesso senza motivo.

Dovresti vedere la risposta di Farm! (Il collegamento all'articolo è interrotto, ma lo risolverò con questo esempio ufficiale dal sequelize: https://github.com/sequelize/express-example/blob/master/models/index.js - puoi sfogliare il intero progetto per farsi un'idea di quello che sta succedendo).

ps sto modificando questo post in quanto è così votato che le persone non vedranno nemmeno nuove risposte (come ho fatto io).

Modifica: ho appena cambiato il link in una copia dello stesso post, ma in una pagina Github


Inoltre, avevo l'impressione che tutti i requiremoduli d nel nodo fossero in un certo senso singleton perché il codice in essi contenuto veniva eseguito una volta e poi memorizzato nella cache, in modo che la prossima volta che li richiedessi ottenessi un riferimento a un oggetto memorizzato nella cache. Non è l'intera immagine?
mkoryak,

1
@mkoryak, hai ragione: tutti i moduli commonjs nel nodo sono effettivamente singoli, poiché il valore restituito viene memorizzato nella cache dopo la prima esecuzione. nodejs.org/api/modules.html#modules_caching
Casey Flynn

2
Quindi, l'esempio potrebbe essere semplificato rimuovendo la parte difficile singleton e semplicemente inserendo module.exports = new OrmClass (). Lo proverò, grazie per il tuo feedback :)
user1778770

2
Nel caso in cui qualcuno avesse il mal di testa che ho avuto, ti salverò. Ho avuto problemi con il codice elencato nell'articolo github incentrato sui percorsi. Ho dovuto aggiungere un. al requisito (in questo modo: var object = require ('.' + modelsPath + "/" + name);) e metti anche un ritorno se name.indexOf ('DS_Store')> -1 in forEach nella funzione init (yay OSX). Spero che aiuti.
jinglesthula,

come menzionato @jinglesthula - ci sono alcune modifiche / bug nell'esempio per il caricamento di file con directory (specialmente se è nidificato altrove). Vorrei anche aggiungere la possibilità di passare le opzioni alle relazioni, poiché sono molto importanti (come il nome della chiave esterna, se può essere nullo, ecc.)
Andrey Popov,

96

SequelizeJS ha un articolo sul loro sito Web che risolve questo problema.

Il collegamento è interrotto, ma puoi trovare il progetto di esempio funzionante qui e sfogliarlo. Vedi la risposta modificata sopra per capire perché questa è una soluzione migliore.

Estratto dall'articolo:

  • modelli / index.js

    L'idea di questo file è configurare una connessione al database e raccogliere tutte le definizioni del modello. Una volta che tutto è a posto, chiameremo il metodo associato su ciascuno dei Modelli. Questo metodo può essere utilizzato per associare il Modello ad altri.

          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
Questo è il modo in cui Sequelize consiglia di farlo. Accetterei questo come la risposta corretta.
jpotts18,

3
Questo va bene, ma non puoi usare un modello dai metodi di istanza di un altro modello, o forse ho perso qualcosa.
mlkmt

1
La pagina non esiste più
Mike Cheel,


3
@mlkmt puoi! Dato che hai accesso alla sequelizevariabile nel tuo file modello, puoi accedere all'altro modello con sequelize.models.modelName.
Guilherme Sehn,

29

Ho creato un pacchetto sequelize-connect per aiutare le persone a risolvere questo problema. Segue la convenzione suggerita da Sequelize qui: http://sequelize.readthedocs.org/en/1.7.0/articles/express/

Inoltre funziona anche un po 'più come Mongoose in termini di interfaccia. Ti consente di specificare una serie di posizioni in cui si trovano i tuoi modelli e ti consente anche di definire una funzione di corrispondenza personalizzata per abbinare i file del tuo modello.

L'utilizzo è sostanzialmente così:

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

Quindi puoi accedere ai modelli e sequelizzare in questo modo:

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

Spero che questo aiuti qualcuno.


3
Il collegamento a un articolo aiuta un po '. Citando alcuni documenti è meglio. Mostrare uno snippet di codice è fantastico .... Ma in realtà costruire una libreria che risolve il problema e metterlo su NPM è fantastico e merita più amore! +1 e sarà protagonista del tuo progetto.
Stijn de Witt,

9

Ho iniziato a utilizzare Sequelize nell'app Express.js. Presto si sono imbattuti in problemi della natura che stai descrivendo. Forse non ho capito bene Sequelize, ma per me fare le cose oltre a selezionare da un solo tavolo non era davvero conveniente. E dove normalmente dovresti usare select da due o più tabelle, o un'unione in puro SQL, dovresti eseguire query separate e con la natura asincrona di Node è solo una maggiore complessità.

Pertanto mi sono allontanato dall'uso di Sequelize. Inoltre sto passando dall'uso QUALSIASI recupero di dati dal DB nei modelli. A mio avviso è meglio estrarre completamente i dati. E i motivi sono: immagina di non utilizzare solo MySQL (nel mio caso, utilizzo MySQL e MongoDB fianco a fianco), ma puoi ottenere i tuoi dati da qualsiasi fornitore di dati e qualsiasi metodo di trasporto, ad esempio SQL, no-SQL, filesystem, API esterne, FTP, SSH ecc. Se si provasse a fare tutto questo nei modelli, alla fine si creerebbe un codice complesso e difficile da comprendere che sarebbe difficile aggiornare e eseguire il debug.

Ora, ciò che si vuole fare è quello di avere modelli di ottenere i dati da uno strato che sa dove e come ottenerlo, ma i tuoi modelli utilizzare solo metodi API, ad esempio fetch, save, deleteecc E dentro questo strato si deve implementazioni specifiche per i fornitori di dati specifici. Ad esempio puoi richiedere determinati dati da un file PHP su un computer locale o dall'API di Facebook o da Amazon AWS o da un documento HTML remoto, ecc.

PS alcune di queste idee sono state prese in prestito da Architect da Cloud9 : http://events.yandex.ru/talks/300/


Questi sono punti validi, ma avrei preferito evitare di reimplementare fetch, save, deleteecc al di fuori del Sequelizedato che il quadro già fornisce i mezzi. È più bello, ma meno conveniente avere un livello di recupero separato. Allo stesso tempo, potresti probabilmente aggiungere uno strato di astrazione di recupero attorno a Sequelize ma poi la soluzione è più complicata, per una vittoria discutibile.
Zorayr,

questo tutorial può essere di grande aiuto: sequelize + express esempio
Lucas Do Amaral,

@ mvbl-fst Hai appena descritto un livello DAO. Supponiamo che tu abbia alcuni utenti in un DB SQL e diversi utenti nel filesystem. Dovresti avere due DAO che astraggono come ottenere ciascuno di essi, quindi un livello aziendale che concatena gli utenti (magari adatta anche alcune proprietà) e li riporti al percorso (il livello di presentazione).
DJDaveMark

5

L'ho impostato come Farm e la documentazione descrive.

Ma avevo il problema aggiuntivo che nei miei metodi di istanza e metodi di classe che avrei collegato ai modelli in ciascuna funzione avrei bisogno di richiedere il file di indice per ottenere un blocco di altri oggetti di database.

Risolto rendendolo accessibile a tutti i modelli.

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

E nel file del modello

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

L'ho fatto solo per i metodi di classe ma potresti anche fare la stessa cosa per i metodi di esempio.


+1 per quella classe prototipo Metodo che restituisce il db. Esattamente l'idea che stavo cercando di essere in grado di caricare classMethods durante la definizione, ma anche di poter fare riferimento a qualsiasi modello in un ClassMethod (ovvero per includere le relazioni)
Bitwit

2

Sto seguendo la guida ufficiale: http://sequelizejs.com/heroku , che ha una cartella dei modelli, imposta ogni modulo in file separati e dispone di un file indice per importarli e impostare la relazione tra loro.


il link non è valido
prisar

2

Sequelize del modello di esempio

'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

Puoi importare modelli da altri file con sequelize.import http://sequelizejs.com/documentation#models-import

In questo modo puoi avere un modulo singleton per il sequelize, che quindi carica tutti gli altri modelli.

In realtà questa risposta è abbastanza simile alla risposta di user1778770.


1
funziona con dipendenze circolari? Ad esempio, quando il modello A ha un FK sul modello B e il modello ha un FK sul modello A
mkoryak,

1

Sto cercando un'app nodejs di esempio che utilizza il sequelize ORM.

Potresti essere interessato a guardare la soluzione del piatto caldaia PEAN.JS.

PEAN.JS è una soluzione open source JavaScript full-stack, che fornisce un solido punto di partenza per applicazioni basate su PostgreSQL, Node.js, Express e AngularJS.

Il progetto PEAN è un fork del progetto MEAN.JS (da non confondere con MEAN.IO o lo stack MEAN generico ).

PEAN sostituisce MongoDB e Mongoose ORM con PostgreSQL e Sequelize. Un vantaggio principale del progetto MEAN.JS è l'organizzazione che fornisce a uno stack che ha molti pezzi in movimento.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.