Devamlılık kullanan bir düğüm uygulaması nasıl organize edilir?


125

ORM'yi devam ettiren bir örnek nodejs uygulaması arıyorum.

Benim asıl endişem, Requ () bağımlılık döngüleri nedeniyle bu modellerin birbirleriyle karmaşık ilişkileri varsa, modellerinizi ayrı js dosyalarında tanımlamanın neredeyse imkansız görünmesidir. Belki insanlar tüm modellerini çok uzun olan tek bir dosyada tanımlarlar?

Esas olarak modellerin uygulama üzerinden nasıl tanımlandığı ve kullanıldığıyla ilgileniyorum. Kendi başıma yaptığım şeyin işleri yapmanın "iyi" yolu olduğunu doğrulamak isterim.


2
Birine yardım edecek bir örnek ekledim github.com/shaishab/sequelize-express-example
Shaishab Roy

Yanıtlar:


125

Kısa öykü

Bu durumda hile modelini başlatmak için değil de dosyaya ama sadece onun başlatma için Gerekli bilgileri temin etmek ve modelleri kurulum ve örnekleme merkezi bir modül icabına edelim.

Yani adımlar:

  • Alanlar, ilişkiler ve seçenekler gibi modelle ilgili verileri içeren birkaç Model dosyasına sahip olun.
  • Tüm bu dosyaları yükleyen ve tüm model sınıflarını ve ilişkilerini ayarlayan tek bir modüle sahip olun.
  • Singleton modülünüzü app.js dosyasında kurun.
  • Singleton modülünden modeli sınıfları alın yoktur kullanmak requireyerine tekiz gelen modeller, modeliniz dosyaları yükleyin.

Uzun hikaye

İşte ilgili kaynak koduyla birlikte bu çözümün daha ayrıntılı bir açıklaması:

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

DÜZENLEME: Bu çok eski bir cevap! (bilgi için okuyun)

Birçok yönden eski ve sınırlı!

  • İlk olarak , @jinglesthula yorumlarda belirtildiği gibi (ve ben de bunu yaşadım) - bu dosyaları zorunlu kılmakla ilgili sorunlar var. Çünkü requireaynı şekilde çalışmıyor readdirSync!

  • İkincisi - ilişkilerde çok sınırlısınız - kod bu ilişkilere seçenekler sunmadığından , özelliğe belongsToManyihtiyaç duyduğu için yaratamazsınız through. En temel assocları yapabilirsiniz.

  • Üçüncüsü - model ilişkilerinde çok sınırlısınız! Kodu yakından okursanız, ilişkilerin bir Dizi yerine bir Nesne olduğunu göreceksiniz , bu nedenle aynı türden birden fazla ilişkilendirme yapmak istiyorsanız (iki kez sahip olmak gibi ) - yapamazsınız!belongsTo

  • Dördüncü - O tekil şeye ihtiyacın yok. Nodejs'deki her modül kendi başına singleton'dur, bu nedenle tüm bu nedenler sebepsiz yere oldukça karmaşıktır.

Farm'ın cevabını görmelisiniz! (Makalenin bağlantısı bozuk, ancak devam ettirme bölümündeki şu resmi örnekle düzelteceğim: https://github.com/sequelize/express-example/blob/master/models/index.js - göz atabilirsiniz neler olup bittiğine dair bir fikir edinmek için tüm proje).

ps Bu gönderiyi o kadar olumlu oy verildiği için düzenliyorum ki insanlar yeni cevaplar bile görmeyecekler (benim yaptığım gibi).

Düzenleme: Bağlantıyı aynı gönderinin bir kopyasına, ancak bir Github Sayfasına değiştirdi


Ayrıca, requiredüğümdeki tüm d modüllerinin bir anlamda tekil olduğu izlenimine kapıldım çünkü içlerindeki kod bir kez çalıştırılır ve sonra önbelleğe alınır, böylece bir dahaki sefere bir önbelleğe alınmış nesne referansı alırsınız. Bu resmin tamamı değil mi?
mkoryak

1
@mkoryak, haklısınız - döndürülen değer ilk çalıştırmadan sonra önbelleğe alındığından düğümdeki tüm ortak js modülleri etkin bir şekilde tekildir. nodejs.org/api/modules.html#modules_caching
Casey Flynn

2
Bu nedenle, örnek singleton zor kısmı kaldırarak basitleştirilebilir ve sadece module.exports = new OrmClass () koyabilir. Deneyeceğim, geri bildiriminiz için teşekkürler :)
user1778770

2
Başım ağrıyan biri olursa diye seni kurtaracağım. Yolları merkez alan github makalesinde listelenen kodla ilgili sorunlar yaşadım. Bir eklemek zorunda kaldım. gerekliliğe (bunun gibi: var object = require ('.' + modelsPath + "/" + name);) ve ayrıca init işlevindeki forEach'e name.indexOf ('DS_Store')> -1 de bir dönüş koyun (yay OSX). Umarım yardımcı olur.
jinglesthula

@jinglesthula'dan bahsedildiği gibi - örnekte dizini içeren dosyaları yüklemek için bazı değişiklikler / hatalar vardır (özellikle başka bir yere yerleştirilmişse). Ayrıca, çok önemli oldukları için (yabancı anahtarın adı, boş bırakılmasına izin verilirse vb.) İlişkilere seçenekler geçirme yeteneği de
eklerdim

96

SequelizeJS'in web sitesinde bu sorunu çözen bir makale var.

Bağlantı bozuk, ancak çalışan örnek projeyi burada bulabilir ve göz atabilirsiniz. Bunun neden daha iyi bir çözüm olduğunu görmek için yukarıdaki düzenlenmiş yanıta bakın.

Makaleden çıkar:

  • Modeller / index.js

    Bu dosyanın amacı, veritabanına bir bağlantı yapılandırmak ve tüm Model tanımlarını toplamaktır. Her şey yerine oturduğunda, Modellerin her biriyle ilişkili yöntemi çağıracağız. Bu yöntem, Modeli başkalarıyla ilişkilendirmek için kullanılabilir.

          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'nin bunu yapmayı önerdiği yol budur. Bunu doğru cevap olarak kabul ediyorum.
jpotts18

3
Bu iyidir, ancak başka bir modelin örnek yöntemlerinden bir modeli kullanamazsınız veya belki bir şeyi gözden kaçırmışımdır.
mlkmt

1
Sayfa artık yok
Mike Cheel


3
@mlkmt yapabilirsiniz! Eğer erişiminiz olmadığından sequelizemodeliniz dosyasında değişken, sen ile başka modeli erişebilirler sequelize.models.modelName.
Guilherme Sehn

29

İnsanların bu sorunu çözmelerine yardımcı olmak için bir sequelize-connect paketi oluşturdum . Burada Sequelize tarafından önerilen kuralı izler: http://sequelize.readthedocs.org/en/1.7.0/articles/express/

Ek olarak, arayüzü açısından Mongoose'a biraz daha benziyor. Modellerinizin bulunduğu bir dizi konum belirlemenize ve ayrıca model dosyalarınızla eşleşecek özel bir eşleme işlevi tanımlamanıza olanak tanır.

Kullanım temelde şu şekildedir:

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

Ardından modellere erişebilir ve şu şekilde devam edebilirsiniz:

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

Umarım bu birisine yardımcı olur.


3
Bir makaleye bağlantı vermek biraz yardımcı olur. Bazı belgelerden alıntı yapmak daha iyidir. Bir kod parçacığını göstermek harikadır ... Ama aslında sorunu çözen ve onu NPM'ye yerleştiren bir kitaplık oluşturmak harika ve daha fazla sevgiyi hak ediyor! +1 ve projenize yıldız verir.
Stijn de Witt

9

Express.js uygulamasında Sequelize kullanmaya başladım. Çok geçmeden tarif ettiğiniz doğa ile ilgili konularla karşılaştık. Belki Sequelize'yi tam olarak anlamadım, ancak bir masadan seçim yapmaktan fazlasını yapmak benim için pek uygun değildi. Ve normalde iki veya daha fazla tablodan seçmeyi veya saf SQL'de bir birleşimi kullanacağınız yerlerde, ayrı sorgular çalıştırmanız gerekir ve Düğümün asenkron doğası ile bu sadece ek karmaşıklıktır.

Bu nedenle Sequelize kullanmaktan uzaklaştım. Dahası, modellerde DB'den HERHANGİ veri getirmeyi kullanıyorum. Bence verileri tamamen soyutlamak daha iyidir. Ve nedenler - sadece MySQL kullanmadığınızı hayal edin (benim durumumda MySQL ve MongoDB'yi yan yana kullanıyorum), ancak verilerinizi herhangi bir veri sağlayıcısından ve SQL, no-SQL gibi herhangi bir taşıma yönteminden alabilirsiniz. dosya sistemi, harici API, FTP, SSH vb. Bunların hepsini modellerde yapmaya çalıştıysanız, sonunda yükseltmesi ve hata ayıklaması zor olacak karmaşık ve anlaşılması zor bir kod yaratırsınız.

Şimdi yapmak istediğiniz şey, modellerin verileri nereden ve nasıl elde edeceğini bilen bir katmandan almasını sağlamaktır, ancak modelleriniz yalnızca API yöntemlerini kullanır, ör. fetch . save, deleteVb. Ve bu katmanın içinde belirli veri sağlayıcıları için özel uygulamalarınız vardır. Örneğin, yerel bir makinedeki bir PHP dosyasından veya Facebook API'den veya Amazon AWS'den veya uzak HTML belgesinden vb. Belirli verileri talep edebilirsiniz.

Not : Bu fikirlerden bazıları Cloud9 tarafından Architect'ten ödünç alındı : http://events.yandex.ru/talks/300/


Bunlar geçerli noktalarıdır, ama yerine reimplementing önleyeceğini fetch, save, deletevs. dışında Sequelizeçerçevesi zaten araç sağlar düşünülürse. Daha güzeldir, ancak ayrı bir getirme katmanına sahip olmak daha az uygundur. Aynı zamanda, muhtemelen Sequelize'nin etrafına çekici bir soyutlama katmanı ekleyebilirsiniz, ancak o zaman çözüm tartışmalı bir kazanç için daha karmaşıktır.
Zorayr

Bu öğretici çok yardımcı olabilir: devamı + ifade örneği
Lucas Do Amaral

@ mvbl-fst Bir DAO katmanını tanımladınız. Diyelim ki bir SQL DB'de bazı kullanıcılarınız ve dosya sisteminde farklı kullanıcılar var. Her birini nasıl elde edeceğinizi özetleyen iki DAO'nuz, ardından kullanıcıları bir araya getiren (hatta bazı özellikleri uyarlayan) ve onları rotanıza (sunum katmanı) geri aktaran bir iş katmanına sahip olmalısınız.
DJDaveMark

5

Çiftlik olarak kurdum ve dokümantasyon açıklıyor.

Ancak örnek yöntemlerimde ve her işlevdeki modellere ekleyeceğim sınıf yöntemlerinde, diğer veritabanı nesnelerini elde etmek için dizin dosyasına ihtiyaç duymam gerektiği gibi ek bir sorun yaşıyordum.

Bunları tüm modeller için erişilebilir hale getirerek çözdü.

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

Ve model dosyasında

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

Bunu sadece sınıf yöntemleri için yaptım ama aynı şeyi örnek yöntemler için de yapabilirsiniz.


Db'yi döndüren bu prototip classMethod için +1. Tam olarak aradığım fikir, tanımlama sırasında classMethods yükleyebilmek ve aynı zamanda bir ClassMethod (yani ilişkileri dahil etmek için) herhangi bir Modele başvurabilmek
bitwit

2

Resmi kılavuzu takip ediyorum: http://sequelizejs.com/heroku , bir modeller klasörü, her modülü ayrı dosyalar halinde kuruyor ve bunları içe aktarmak ve aralarındaki ilişkiyi ayarlamak için bir dizin dosyasına sahip.


bağlantı geçerli değil
prisar

2

Örnek model devamı

'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

Diğer dosyalardan modelleri http://sequelizejs.com/documentation#models-import ile içe aktarabilirsiniz.sequelize.import

Bu şekilde, ardışık hale getirmek için tek bir modüle sahip olabilirsiniz, bu daha sonra tüm diğer modelleri yükler.

Aslında bu cevap user1778770'in cevabına oldukça benziyor.


1
bu döngüsel bağımlılıklar ile çalışır mı? Örneğin, A modelinde B modeline bir FK varsa ve model A modeline bir
FK'ye

1

ORM'yi devam ettiren bir örnek nodejs uygulaması arıyorum.

PEAN.JS standart şablon çözümüne bakmak ilginizi çekebilir.

PEAN.JS, PostgreSQL, Node.js, Express ve AngularJS tabanlı uygulamalar için sağlam bir başlangıç ​​noktası sağlayan tam yığın JavaScript açık kaynaklı bir çözümdür.

PEAN projesi, MEAN.JS projesinin bir çatalıdır (MEAN.IO veya genel MEAN yığını ile karıştırılmamalıdır ).

PEAN, MongoDB ve Mongoose ORM'yi PostgreSQL ve Sequelize ile değiştiriyor. MEAN.JS projesinin birincil faydası, çok sayıda hareketli parçası olan bir yığına sağladığı organizasyondur.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.