Como organizar um aplicativo de nó que usa sequelizar?

125

Estou procurando um exemplo de aplicativo nodejs que usa o ORM sequelizado.

Minha principal preocupação é que parece quase impossível definir seus modelos em arquivos js separados se esses modelos tiverem relacionamentos complexos entre si devido a loops de dependência require (). Talvez as pessoas definam todos os seus modelos em um arquivo que é muito, muito longo?

Estou interessado principalmente em como os modelos são definidos e usados ​​através do aplicativo. Gostaria de ter alguma validação de que o que estou fazendo por conta própria é a "boa" maneira de fazer as coisas.

mkoryak
fonte
2
Eu adicionei um exemplo que pode vai ajudar alguém github.com/shaishab/sequelize-express-example
Shaishab Roy
Eu escrevi um artigo sobre a nossa solução: medium.com/@ismayilkhayredinov/…
hypeJunction

Respostas:

125

O conto

O truque neste caso não é inicializar o modelo no arquivo, mas apenas fornecer as informações necessárias para sua inicialização e permitir que um módulo centralizado cuide da configuração e instanciação do modelo.

Portanto, as etapas são:

  • Tenha vários arquivos de modelo com dados sobre o modelo, como campos, relacionamentos e opções.
  • Tenha um módulo singleton que carregue todos esses arquivos e configure todas as classes e relacionamentos de modelo.
  • Configure seu módulo singleton no arquivo app.js.
  • Obtenha as classes de modelo do módulo singleton , não use requireem seus arquivos de modelo, carregue os modelos a partir do singleton.

A história mais longa

Aqui está uma descrição mais detalhada desta solução com o código-fonte correspondente:

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

EDIT: Esta é uma resposta muito antiga! (leia para informações)

É antigo e limitado de várias maneiras!

  • Primeiro , como o @jinglesthula mencionado nos comentários (e eu também o experimentei) - há problemas ao exigir esses arquivos. É porque requirenão funciona da mesma maneira que readdirSync!

  • Segundo - você é muito limitado nas relações - o código não fornece opções para essas associações, portanto , NÃO PODE criar belongsToMany, pois precisa de throughpropriedade. Você pode fazer as associações mais básicas.

  • Terceiro - você é muito limitado nas relações modelo! Se você ler atentamente o código, verá que as relações são um Objeto em vez de uma Matriz ; portanto, se você quiser fazer mais de uma associação do mesmo tipo (como duas vezes belongsTo) - não poderá!

  • Quarto - Você não precisa dessa coisa única. Cada módulo no nodejs é único por si só, então tudo isso torna-se bastante complexo sem motivo.

Você deve ver a resposta da fazenda! (O link para o artigo está quebrado, mas vou corrigi-lo com este exemplo oficial de sequelize: https://github.com/sequelize/express-example/blob/master/models/index.js - você pode navegar no todo o projeto para ter uma idéia do que está acontecendo).

ps Estou editando este post porque é tão votado que as pessoas nem veem novas respostas (como eu).

Editar: acabou de alterar o link para uma cópia da mesma postagem, mas em uma página do Github

user1778770
fonte
Além disso, tive a impressão de que todos os requiremódulos d no nó eram, em certo sentido, singletons porque o código neles é executado uma vez e depois armazenado em cache, para que da próxima vez que você precisar deles, esteja recebendo uma referência a objeto em cache. Não é este o quadro todo?
Mkoryak
1
@mkoryak, você está certo - todos os módulos commonjs no nó são efetivamente singletons, pois o valor retornado é armazenado em cache após a primeira execução. nodejs.org/api/modules.html#modules_caching
Casey Flynn
2
Portanto, o exemplo pode ser simplificado removendo a parte complicada de singleton e basta colocar module.exports = new OrmClass (). Vou testá-lo, obrigado por seu feedback :)
user1778770
2
Caso alguém tenha a dor de cabeça que eu tive, eu vou te salvar. Eu tive problemas com o código listado no artigo do github, centrado nos caminhos. Eu tive que adicionar um. para o require (como este: var object = require ('.' + modelsPath + "/" + name);) e também coloque um retorno se name.indexOf ('DS_Store')> -1 no forEach na função init (sim OSX). Espero que ajude.
precisa saber é o seguinte
como o @jinglesthula mencionou - há algumas alterações / bugs no exemplo para carregar arquivos dentro do diretório (especialmente se ele estiver aninhado em outro lugar). Gostaria de acrescentar também a capacidade de passar opções para as relações, como eles são muito importantes (como o nome da chave estrangeira, se ele está autorizado a ser nula, etc.)
Andrey Popov
96

SequelizeJS tem um artigo em seu site que resolve esse problema.

O link está quebrado, mas você pode encontrar o projeto de exemplo aqui e navegar nele. Veja a resposta editada acima para ver por que essa é uma solução melhor.

Extrato do artigo:

  • models / index.js

    A idéia desse arquivo é configurar uma conexão com o banco de dados e coletar todas as definições de modelo. Quando tudo estiver no lugar, chamaremos o método associado em cada um dos modelos. Este método pode ser usado para associar o modelo a outros.

          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)
Fazenda
fonte
12
É assim que Sequelize recomenda fazer isso. Eu aceitaria isso como a resposta correta.
jpotts18
3
Isso é bom, mas você não pode usar um modelo dos métodos de instância de outro modelo, ou talvez eu tenha perdido algo.
mlkmt
1
A página não existe mais #
Mike Cheel
1
Aqui está o link de trabalho: sequelize.readthedocs.org/en/1.7.0/articles/express
chrisg86
3
@mlkmt você pode! Como você tem acesso à sequelizevariável em seu arquivo de modelo, é possível acessar seu outro modelo com sequelize.models.modelName.
Guilherme Sehn 15/10
29

Criei um pacote sequelize-connect para ajudar as pessoas a lidar com esse problema. Segue a convenção sugerida Sequelize aqui: http://sequelize.readthedocs.org/en/1.7.0/articles/express/

Além disso, também funciona um pouco mais como o Mongoose em termos de sua interface. Ele permite que você especifique um conjunto de locais onde seus modelos estão localizados e também permite definir uma função de correspondência personalizada para corresponder aos arquivos de modelo.

O uso é basicamente assim:

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

Em seguida, você pode acessar os modelos e sequenciar da seguinte forma:

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

Espero que isso ajude alguém.

jspizziri
fonte
3
Vincular a um artigo ajuda um pouco. Citar alguns documentos é melhor. Mostrar um trecho de código é ótimo ... Mas criar uma biblioteca que resolva o problema e colocá-lo no NPM é fantástico e merece mais amor! +1 e estrelará seu projeto.
Stijn de Witt
9

Comecei a usar o Sequelize no aplicativo Express.js. Em breve, surgiram problemas da natureza que você está descrevendo. Talvez eu não tenha entendido o Sequelize, mas fazer coisas mais do que apenas selecionar uma tabela não era realmente conveniente. E onde normalmente você usaria select de duas ou mais tabelas, ou uma união em SQL puro, teria que executar consultas separadas e, com a natureza assíncrona do Node, isso apenas aumentaria a complexidade.

Portanto, deixei de usar o Sequelize. Além disso, estou mudando de usar QUALQUER busca de dados do DB nos modelos. Na minha opinião, é melhor abstrair a obtenção de dados completamente. E as razões são: imagine que você não use apenas o MySQL (no meu caso, eu uso o MySQL e o MongoDB lado a lado), mas você pode obter seus dados de qualquer provedor de dados e qualquer método de transporte, por exemplo, SQL, no-SQL, sistema de arquivos, API externa, FTP, SSH etc. Se você tentasse fazer tudo isso nos modelos, acabaria criando um código complexo e difícil de entender que seria difícil de atualizar e depurar.

Agora, o que você quer fazer é ter modelos obter dados de uma camada que sabe onde e como obtê-lo, mas seus modelos só usam métodos API, por exemplo fetch, save, deleteetc. E dentro desta camada que têm implementações específicas para provedores de dados específicos. Por exemplo, você pode solicitar determinados dados de um arquivo PHP em uma máquina local ou da API do Facebook ou do Amazon AWS ou de documento HTML remoto etc.

PS: algumas dessas idéias foram emprestadas do Architect pela Cloud9 : http://events.yandex.ru/talks/300/

mvbl fst
fonte
Estes são pontos válidos, mas eu preferia evitar reimplementar fetch, save, deleteetc. fora Sequelizedado que o quadro já fornece os meios. É melhor, mas menos conveniente ter uma camada de busca separada. Ao mesmo tempo, você provavelmente pode adicionar uma camada de abstração em torno do Sequelize, mas a solução é mais complicada, para uma vitória discutível.
Zorayr 29/09/16
este tutorial será muito útil: sequenciar + exemplo expresso
Lucas Do Amaral
@ mvbl-fst Você acabou de descrever uma camada DAO. Digamos que você tenha alguns usuários em um banco de dados SQL e usuários diferentes no sistema de arquivos. Você deve ter dois DAOs que abstraem como obter cada um deles, depois uma camada de negócios que concatena os usuários (talvez até adapte algumas propriedades) e os transmite de volta à sua rota (a camada de apresentação).
DJDaveMark
5

Eu o configurei como Farm e a documentação descreve.

Mas eu estava tendo o problema adicional de que, na minha instância, métodos e métodos de classe que eu anexaria aos modelos em cada função, seria necessário exigir que o arquivo de índice conseguisse outros objetos de banco de dados.

Resolvido, tornando-os acessíveis a todos os modelos.

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 no arquivo de modelo

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

Eu fiz isso apenas para os métodos de classe, mas você também pode fazer o mesmo por exemplo.

jacob
fonte
+1 para o protótipo classMethod que retorna o banco de dados. Exatamente a idéia que eu estava procurando para poder carregar o classMethods durante o define, mas também ser capaz de referenciar qualquer modelo em um ClassMethod (por exemplo, para incluir relacionamentos)
bitwit
2

Estou seguindo o guia oficial: http://sequelizejs.com/heroku , que possui uma pasta de modelos, configure cada módulo em arquivos separados e tenha um arquivo de índice para importá-los e definir o relacionamento entre eles.

Ron
fonte
link não é válido
prisar 12/04/19
2

Sequenciar modelo de amostra

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


Muhammad Arief Trimanda
fonte
1

Você pode importar modelos de outros arquivos com sequelize.import http://sequelizejs.com/documentation#models-import

Dessa forma, você pode ter um módulo singleton para sequenciar, que carrega todos os outros modelos.

Na verdade, esta resposta é bastante semelhante à resposta do usuário1778770.

natrixnatrix89
fonte
1
isso funciona com dependências circulares? Por exemplo, quando o modelo A tem uma FK para o modelo B e modelo seja tem uma FK para o modelo A
mkoryak
1

Estou procurando um exemplo de aplicativo nodejs que usa o ORM sequelizado.

Você pode estar interessado em olhar para a solução padrão PEAN.JS.

O PEAN.JS é uma solução de código aberto JavaScript de pilha completa, que fornece um sólido ponto de partida para aplicativos baseados em PostgreSQL, Node.js, Express e AngularJS.

O projeto PEAN é uma bifurcação do projeto MEAN.JS (não deve ser confundido com o MEAN.IO ou a pilha genérica do MEAN).

O PEAN substitui o MongoDB e o Mongoose ORM pelo PostgreSQL e Sequelize. Um benefício primário do projeto MEAN.JS é a organização que ele fornece a uma pilha que possui muitas peças móveis.

mg1075
fonte
0

Você também pode usar uma injeção de dependência que fornece uma solução elegante para isso. Aqui está um https://github.com/justmoon/reduct

Vahe Hovhannisyan
fonte