在Sequelize中使用遷移

Sequelize是Nodejs生態中一個比較出名的ORM框架。經過ORM框架,可使用對象來操做數據庫表數據,提升了開發效率和代碼可讀性,也方便後期維護。node

今天主要介紹經過遷移[Migration]來建立數據庫,表。mysql

遷移的好處,能夠類比git。經過每次建立遷移文件,來支持更新,回滾數據庫表結構,也方便協同開發,也避免人工手動去直接修改數據庫,用代碼自動管理。換個電腦,也不用去拷貝數據庫,直接運行遷移就能夠徹底恢復開發環境,極大減輕了心智負擔。git

1. 建立項目, 安裝node package依賴

mkdir node_work

cd node_work

mkdir app

npm init -y

npm i sequelize-cli sequelize mysql2 koa
複製代碼

2. 初始化Sequelize

npx sequelize init
複製代碼

運行以後,會產生四個目錄:sql

config, migrations, models, seeders數據庫


config:npm

{
  "development": {
    "username": "root",
    "password": "root",
    "database": "app_development",
    "host": "127.0.0.1",
    "port": 8889,
    "dialect": "mysql",
    "timezone": "+08:00"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "app_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "app_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}
複製代碼

環境env => {配置}json

不一樣環境,對應不一樣的配置,也能夠自定義環境,好比homepromise

env指的是process.env.NODE_ENVbash

能夠經過設置環境變量來改變,好比export NODE_ENV=productioncookie

遷移時候,也能夠指定環境:npx sequelize db:migrate --env production,來鏈接production對應配置的數據庫

建立數據庫:

npx sequelize db:create
複製代碼

說明npx是npm5.2以後,自帶的一個命令。能夠不用全局安裝sequelize,使用時候,若是本地沒有,就去npm倉庫下載;下載完後或者本地已經下載過,就運行腳本命令。這樣能夠避免本地全局包過時,環境問題,每次都使用最新版本


migrations: 遷移文件

npx sequelize model:generate --name User --attributes username:string
複製代碼

執行後,會生成20180918055558-create-user.js遷移文件,和models/user.js模型文件

其餘字段能夠在遷移文件中補全,最後再運行npx sequelize db:migrate,就能夠在數據庫中看到生成了users表

'use strict';
  module.exports = {
    up: (queryInterface, Sequelize) => {
      return queryInterface.createTable('Users', {
        id: {
          allowNull: false,
          autoIncrement: true,
          primaryKey: true,
          type: Sequelize.INTEGER
        },
        username: {
          type: Sequelize.STRING(20),
          allowNull: false
        },
        password: {
          type: Sequelize.CHAR(32),
          allowNull: false
        },
        createdAt: {
          allowNull: false,
          type: Sequelize.DATE
        },
        updatedAt: {
          allowNull: false,
          type: Sequelize.DATE
        }
      }, {
        tableName: 'users',
        charset: 'utf8mb4',
        collate: 'utf8mb4_bin',
        define: {
          timestamps: true
        }
      }).then(() => {
        // 添加索引
        return queryInterface.addIndex('users', {
          name: 'username',
          unique: true,
          fields: ['username']
        });
      });
    },
    
    // 回退時執行,刪除表
    down: (queryInterface, Sequelize) => {
      return queryInterface.dropTable('Users');
    }
  };
複製代碼

執行遷移:

npx sequelize db:migrate

npx sequelize db:migrate:all
複製代碼

撤銷遷移:

npx sequelize db:migrate:undo 最近一次的

npx sequelize db:migrate:undo:all

npx sequelize db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
複製代碼

--from, --to 參數,能夠指定遷移文件


models: 模型文件

model:generate生成的model都在這個目錄中

'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      username: {
        type: Sequelize.STRING(20),
        allowNull: false
      },
      password: {
        type: Sequelize.CHAR(32),
        allowNull: false
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    }, 
    {
      tableName: 'users',
      charset: 'utf8mb4',
      collate: 'utf8mb4_bin',
    }).then(() => {
      return queryInterface.addIndex('users', {
        name: 'username',
        unique: true,
        fields: ['username']
      });
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('Users');
  }
};
複製代碼

模型對象建立,默認會自動賦值,更新createdAt, updatedAt兩個timestamps字段。下邊會給出完整示例。


seeders: 填充數據文件

建立seed文件:

npx sequelize seed:generate --name demo-user
複製代碼

執行以後,會獲得20180918090545-demo-user.js

'use strict';

const md5 = require('md5')

module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.bulkInsert('Users', [
      {
        username: 'Kimoo',
        password: md5('123456'),
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        username: 'Reci',
        password: md5('123321'),
        createdAt: new Date(),
        updatedAt: new Date(),
      }
    ], {});
  },

  down: (queryInterface, Sequelize) => {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkDelete('Person', null, {});
    */
    return queryInterface.bulkDelete('Users', null, {});
  }
};

複製代碼

填充數據:

npx sequelize db:seed:all

複製代碼

撤銷數據:

npx sequelize db:seed:undo 最近一次的

npx sequelize db:seed:undo --seed name-of-seed-as-in-data 具體某個

npx sequelize db:seed:undo:all

複製代碼

3. 具體實踐

app.js

(async function() {

    const Koa = require('koa');
    const KoaStaticCache = require('koa-static-cache');
    const KoaBodyParser = require('koa-bodyparser');
    const router = require('./routers/main');
    const Session = require('koa-session');

    const app = new Koa();

    // app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

    app.keys = ['app'];

    app.use( Session({
        key: 'koa:sess',
        maxAge: 86400000,
        autoCommit: true,
        overwrite: true,
        httpOnly: true,
        signed: true,
        rolling: false,
        renew: false
    }, app) );

    // app.use( async (ctx, next) => {
    //     ctx.set('Access-Control-Allow-Origin','*');
    //     await next();
    // } );

    app.use( KoaStaticCache('./public', {
        prefix: 'public',
        gzip: true
    }) );

    app.use( KoaBodyParser() );

    app.use( router.routes() );

    app.listen(8088);

})();
複製代碼

models/index.js

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

// 自動導入 models 文件夾下的全部文件,好比user.js這個模型文件

// 自動加載模型並執行
// let users = require('./users');
// let UsersModel = users(sequelize, Sequelize);
// db[UsersModel.name] = UsersModel; // db['Users'] = UsersModel;


// 下面經過fs自動加載全部的文件,並執行,同時生成的模型對象掛載到db對象下面,最後返回出去
fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
複製代碼

routers/main.js

const KoaRouter = require('koa-router');
const md5 = require('md5');
const Models = require('../models');
const Sequelize = require('sequelize');

const router = new KoaRouter();

router.post('/register', async ctx => {

    // console.log(ctx.request.body);
    let username = ctx.request.body.username.trim();
    let password = ctx.request.body.password.trim();
    let repassword = ctx.request.body.repassword.trim();

    if (username=='' || password == '' || repassword == '') {
        return ctx.body = {
            code: 1,
            data: '用戶名或密碼不能爲空'
        }
    }
    if (password != repassword) {
        return ctx.body = {
            code: 2,
            data: '兩次輸入的密碼不一致'
        }
    }

    let user = await Models.Users.findOne({
        where: {
            username
        }
    });

    if (user !== null) {
        return ctx.body = {
            code: 3,
            data: '當前用戶已經被註冊了'
        }
    }

    let newUser = await Models.Users.build({
        username,
        password: md5(password)
    }).save();

    ctx.body = {
        code: 0,
        data: {
            id: newUser.get('id'),
            username: newUser.get('username')
        }
    }

  });

  router.post('/login', async ctx => {
      let username = ctx.request.body.username;
      let password = ctx.request.body.password;

      let user = await Models.Users.findOne({
          where: {
              username
          }
      });

      if (user === null) {
          return ctx.body = {
              code: 1,
              data: '不存在該用戶'
          }
      }

      if (user.get('password') !== md5(password)) {
          return ctx.body = {
              code: 1,
              data: '密碼錯誤'
          }
      }

      
      // ctx.cookies.set('uid', user.get('id'), {
      //     httpOnly: false
      // });

      // 服務端發送一個約定好的cookie,來表示當前是登陸
      // ctx.cookies.set('uid', user.get('id'), {
      //     // httpOnly,表示當前的cookie是否容許客戶端進行操做(js),若是爲true,那麼就表示這個cookie是能用戶http協議的數據傳輸
      //     httpOnly: true,
      //     signed: true
      // });
      ctx.cookies.set('username', user.get('username'), {
          httpOnly: false
      });

      ctx.session.uid = 1;

      ctx.body = {
          code: 0,
          data: {
              id: user.get('id'),
              username: user.get('username')
          }
      }
  });

})

module.exports = router;
複製代碼

4. 測試接口,註冊用戶,添加數據

能夠在postman中測試接口,地址http://localhost:8088/register,註冊用戶

node app.js
複製代碼

相關文章
相關標籤/搜索