专栏首页前端知否在Sequelize中使用迁移

在Sequelize中使用迁移

Sequelize是Nodejs生态中一个比较出名的ORM框架。通过ORM框架,可以使用对象来操作数据库表数据,提高了开发效率和代码可读性,也方便后期维护。

今天主要介绍通过迁移[Migration]来创建数据库,表。

迁移的好处,可以类比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

运行之后,会产生四个目录:

config, migrations, models, seeders

config:

{
  "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 => {配置}

不同环境,对应不同的配置,也可以自定义环境,比如home

env指的是process.env.NODE_ENV

可以通过设置环境变量来改变,比如export NODE_ENV=production

迁移时候,也可以指定环境: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

本文分享自微信公众号 - 前端知否(qianduanzhifou),作者:QETHAN

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JS对象那些事儿

    JavaScript中几乎所有东西都是一个对象,除了六种基本类型数据 - null,undefined,strings,numbers,boolean和symb...

    前端知否
  • React Hooks - 缓存记忆

    React Hooks几乎在所有方面都能让我们在编程中获得好处。但是某些时候的性能问题,也需要使用一些技巧来解决。我们可以使用Hooks编写快速的应用程序,但是...

    前端知否
  • Sass->什么时候使用Mixins 和 Placeholders

    原文:https://www.sitepoint.com/sass-mixin-placeholder/

    前端知否
  • 博客园LaTex的测试,附带开启方法

    单位矩阵是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。其他全都为0,eg:

    逸鹏
  • Python学习笔记5—Python模块

        默认情况下,python第三方的模块安装在python 的安装目录下site-packages下,以文件或者目录的形式存放

    py3study
  • docker虚拟化之订制python环境

    前面我们讲了python爬虫用到的工具及模块:phantomjs、beautifulsoup4、selenium、lxml等,如果我们想随时随地用到这个已经搭建...

    py3study
  • 机器学习第3天:多元线性回归

    简单线性回归:影响Y的因素唯一,只有一个。 多元线性回归:影响Y的因数不唯一,有多个。

    明天依旧可好
  • 聊聊hikari连接池的leakDetectionThreshold

    本文主要研究一下hikari连接池的leakDetectionThreshold,也就是连接池泄露检测。

    codecraft
  • Recyclerview中使用databinding完成多布局

    其实目前使用Recyclerview的关键部分在于adapter如何去编写,网上也有很多大神封装了各种万能adapter来供大家使用,但是对于我们这些新手,如果...

    坑吭吭
  • 研究rbd挂载之后ext4文件损坏的问题 原

    http://docs.ceph.com/docs/master/rbd/rbd-config-ref/

    domain0

扫码关注云+社区

领取腾讯云代金券