Як організувати додаток для вузла, який використовує секелізацію?


125

Я шукаю приклад програми nodejs, яка використовує секелізацію ORM.

Моє основне занепокоєння полягає в тому, що, здається, неможливо визначити ваші моделі в окремих js-файлах, якщо ці моделі мають складні відносини один до одного через петлі залежності (). Можливо, люди визначають усі свої моделі в одному файлі, який дуже-дуже довгий?

Мене в основному цікавить, як визначаються та використовуються моделі за допомогою програми. Мені хотілося б підтвердити, що те, що я роблю самостійно - це «хороший» спосіб робити речі.


2
Я додав приклад, який може допомогти комусь github.com/shaishab/sequelize-express-example
Shaishab Roy

Я написав статтю про наше рішення: medium.com/@ismayilkhayredinov/…
hypeJunction

Відповіді:


125

Новела

Хитрість у цьому випадку - не ініціалізувати модель у файлі, а просто надати необхідну інформацію для її ініціалізації та дозволити централізованому модулю подбати про налаштування та інстанціювання моделей.

Отже, кроки такі:

  • Майте кілька модельних файлів з даними про модель, як-от поля, відносини та параметри.
  • Майте однотонний модуль, який завантажує всі ці файли та налаштовує всі класи та відносини моделі.
  • Налаштуйте свій однотонний модуль у файлі app.js.
  • Отримайте класи моделей з модуля singleton, які не використовуються requireу файлах ваших моделей, завантажте моделі з singleton.

Більш довга історія

Ось більш детальний опис цього рішення з відповідним вихідним кодом:

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

EDIT: Це дуже стара відповідь! (читайте для інформації)

Це старе і багато в чому обмежене!

  • По-перше , як у коментарях згадував @jinglesthula (і я це теж відчував) - виникають проблеми з необхідністю цих файлів. Це тому, requireщо не працює так само, як readdirSync!

  • По-друге - ви дуже обмежені у відносинах - код не надає варіантів для цих асоціацій, тому ви НЕ МОЖЕТЕ створювати так, belongsToManyяк йому потрібно throughвластивість. Ви можете зробити найосновніші доценти.

  • По-третє - ви дуже обмежені в модельних відносинах! Якщо ви уважно прочитаєте код, ви побачите, що відносини - це Об'єкт, а не масив , тому якщо ви хочете зробити більше одного об'єднання одного типу (наприклад, мати два рази belongsTo) - ви не можете!

  • Четверте - вам не потрібна ця одинока річ. Кожен модуль у nodejs сам по собі є однотонним, тому все це робить досить складним без жодних причин.

Ви повинні побачити відповідь Ферми! (Посилання на статтю порушено, але я виправлю це офіційний зразок із секвелізації: https://github.com/sequelize/express-example/blob/master/models/index.js - ви можете переглядати цілий проект, щоб отримати уявлення про те, що відбувається).

ps Я редагую цю публікацію, оскільки вона настільки обґрунтована, що люди навіть не побачать нових відповідей (як я).

Редагувати: Просто змінив посилання на копію тієї самої публікації, але на сторінці Github


Крім того, у мене склалося враження, що всі required модулі у вузлі в певному сенсі є одинаковими, тому що код в них виконується один раз, а потім кешується, так що наступного разу, коли ви вимагаєте їх, ви отримуєте кешоване посилання на об'єкт. Хіба це не вся картина?
mkoryak

1
@mkoryak, ви праві - всі модулі commonjs у вузлі фактично є однотонними, оскільки повернене значення кешується після першого виконання. nodejs.org/api/modules.html#modules_caching
Кейсі Флінн

2
Отже, приклад можна спростити, видаливши складну частину одиночної форми та просто поставивши module.exports = new OrmClass (). Я спробую це, дякую за ваш відгук :)
користувач1778770

2
На всякий випадок, якщо у когось болить голова, я врятую вас. У мене виникли проблеми з кодом, переліченим у статті про github, яка зосереджена навколо шляхів. Мені довелося додати. до вимоги (як-от: var object = requ ('.' + modelsPath + "/" + name);), а також поставити return, якщо name.indexOf ('DS_Store')> -1 у forEach у функції init (так OSX). Сподіваюся, що це допомагає.
jinglesthula

як згадував @jinglesthula - у прикладі є деякі зміни / помилки для завантаження файлів у каталогinging (особливо якщо він вкладений десь в іншому місці). Я також додав би можливість передавати варіанти відносинам, оскільки вони дуже важливі (наприклад, ім'я зовнішнього ключа, якщо це дозволено бути недійсним тощо)
Андрій Попов

96

SequelizeJS має статтю на своєму веб-сайті, яка вирішує цю проблему.

Посилання розірвано, але ви можете знайти робочий зразок проекту тут і переглянути його. Дивіться відредаговану відповідь вище, щоб дізнатися, чому це краще рішення.

Витяг із статті:

  • моделі / 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

3
Це добре, але ви не можете використовувати модель з методів інстанції іншої моделі, або, можливо, я щось пропустив.
mlkmt

1
Сторінка вже не існує
Майк Чіл

1
Ось робоче посилання: sequelize.readthedocs.org/en/1.7.0/articles/express
chrisg86

3
@mlkmt ви можете! Оскільки у вас є доступ до sequelizeзмінної у вашому файлі моделі, ви можете отримати доступ до іншої моделі за допомогою sequelize.models.modelName.
Гільгерме Сен

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 і позначте ваш проект.
Штійн де Вітт

9

Я почав використовувати Sequelize в додатку Express.js. Незабаром стикалися з питаннями характеру, який ви описуєте. Можливо, я не зовсім зрозумів Sequelize, але мені робити щось більше, ніж просто вибирати з однієї таблиці було не дуже зручно. І там, де зазвичай ви використовуєте select з двох або більше таблиць або об'єднання в чистому SQL, вам доведеться запускати окремі запити, а з асинхронним характером Node це просто додає складності.

Тому я відмовився від використання Sequelize. Більше того, я переходжу від використання БУДЬ-якого отримання даних з БД в моделях. На мою думку, краще отримати абстрактні дані повністю. І причини - уявіть, що ви не просто використовуєте MySQL (у моєму випадку я використовую MySQL та MongoDB поряд), але ви можете отримати свої дані від будь-якого постачальника даних та будь-якого способу транспортування, наприклад, SQL, no-SQL, файлова система, зовнішній API, FTP, SSH і т. д. Якщо б ви намагалися зробити все це в моделях, з часом ви створили б складний і важкий для розуміння код, який буде важко оновити та налагодити.

Тепер то , що ви хочете зробити , це є моделі отримують дані з шару , який знає , де і як його отримає, але ваші моделі тільки використовувати методи API, наприклад fetch, save, і deleteт.д. І всередині цього шару у вас є конкретні реалізації для конкретних постачальників даних. Наприклад, ви можете запросити певні дані з PHP-файлу на локальній машині або з API API, від Amazon AWS або з віддаленого HTML-документа тощо.

PS деякі з цих ідей були запозичені у архітектора по Cloud9 : http://events.yandex.ru/talks/300/


Це дійсні пункти, але я волів би уникнути реалізувавши fetch, save, і deleteт.д. поза Sequelizeвраховуючи , що структура вже надає кошти. Приємніше, але менш зручно мати окремий шар для отримання. У той же час, ви, ймовірно, можете додати витягнутий шар абстракції навколо Sequelize, але тоді рішення буде складніше, для сперечальної виграші.
Зорайр

цей підручник буде дуже корисним: секелюй + експрес-приклад
Лукас До Амарал

@ mvbl-fst Ви тільки що описали шар DAO. Скажімо, у файловій системі є кілька користувачів у SQL БД та різні користувачі. У вас повинно бути два DAO, які резюмують, як отримати кожен з них, потім бізнес-шар, який об'єднує користувачів разом (можливо, навіть адаптує деякі властивості) і передає їх назад у ваш маршрут (шар презентації).
DJDaveMark

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

Я робив це лише для методів класу, але ви можете зробити те ж саме для методів, наприклад.


+1 для прототипу classMethod, який повертає db. Саме ідея, яку я шукав, зможу завантажувати classMethods під час визначення, але також мати можливість посилатися на будь-яку модель в ClassMethod (тобто для включення відносин)
bitwit

2

Я слідую за офіційним посібником: http://sequelizejs.com/heroku , який має папку з моделями, налаштовує кожен модуль в окремі файли та індексний файл для імпорту та встановлення взаємозв'язку між ними.


посилання недійсне
присар

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

Ви можете імпортувати моделі з інших файлів за допомогою sequelize.import http://sequelizejs.com/documentation#models-import

Таким чином, ви можете мати один однотонний модуль для секселізації, який потім завантажує всі інші моделі.

Насправді ця відповідь досить схожа на відповідь користувача1778770.


1
це працює з круговими залежностями? Наприклад, коли модель A має FK до моделі B, а модель be має FK до моделі A
mkoryak

1

Я шукаю приклад програми nodejs, яка використовує секелізацію ORM.

Можливо, вам буде цікаво переглянути рішення щодо котлів PEAN.JS.

PEAN.JS - це повноцінне рішення з відкритим кодом JavaScript, яке забезпечує надійну вихідну точку для додатків на основі PostgreSQL, Node.js, Express та AngularJS.

Проект PEAN - це вилка проекту MEAN.JS (не плутати з MEAN.IO або загальним стеком MEAN).

PEAN замінює MongoDB та Mongoose ORM на PostgreSQL та Sequelize. Основна перевага проекту MEAN.JS - це організація, яку він надає стеку, який містить багато рухомих фрагментів.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.