• Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API


    继续开扒我们的MEAN栈开发之路,前面两节我们学习了Express、Jade引擎并创建了几个静态页面,最后通过Heroku部署了应用。

    Nodejs之MEAN栈开发(一)---- 路由与控制器

    Nodejs之MEAN栈开发(二)----视图与模型

    这一节将重点介绍MongoDB以及它的ODM(Object-Document Modeler)Mongoose。MongoDB是一种分布式文档存储型数据库,和平时使用的关系型数据库不同,它存储的是BSON格式(json的二进制),特点是高性能、易部署,易使用...(详情请见百科),主要一点它支持JavaScript读写,MEAN栈开发的最大特点就是前后端包括数据库都是JavaScript编写。而Mongoose可以类比EntityFramwork(ORM-对象关系映射),主要做用是让开发人员可以更方便的操作数据库。在开始之前,记得先安装mongodb

    一、MongoDB准备工作

    1. mongo安装完之后,用cmd切到安装目录下面的bin目录。执行mongo,如果出现版本号则说明启动成功:

     

    如果是出现“计算机积极拒绝”,需要设置下dbpath,也就是指定数据的路径。

    mongod --dbpath=c:mongodbdb

    成功之后,可以访问http://localhost:28017/ ,可以看到如下页面:

    说明一切就绪。

    2.为避免每次开机完都要设置dbpath,需要将mongo设置为服务启动。分两步,先用管理员身份打开cmd,切到mongo的bin目录:

    D:mongodbin>mongod --dbpath=D:mongodb --logpath=D:mongodblog.txt --install

    以上路径更换成自己的路径,然后在运行框中输入services.msc。设置服务的时候要确保mongo在运行状态,不然设置不会成功。

    打开文件,找到Mongo DB:

    右键选择启动,这样每次开机mongo就会自动运行。

    3.更方便点,可以在桌面创建一个star.bat文件:

    @echo off
    start "" "D:mongodb-win32-i386-2.0.6inmongo.exe"

     这样每次点击直接进入命令框:

     

    4.常用命令/基本操作 

    查看db:

    show dbs

    切换到某个数据库:

    switched to db dbname
    或者:
    use dbname

    查看某个数据库下的集合

    show collections

    查看集合下面的文档:

    db.collections.find()

     

    因为这一节的重点不是要介绍如何在cmd中操作mongo,所以不再赘述了。更多这样的细节可以参考一线码农的博客:8天学通mongoDB。如果是没有安装sp1的电脑,官网的3.0版本安装成功后无法运行,可以用2.xx免安装版。接下来主要讲Mongoose的操作。

    二、Mongoose

    1.安装

    你可以用npm直接安装:

    也可以用vs进行安装。右键工程上的npm,选择Install New npm Packages。

    第一次会下载安装一个工具,安装完成之后,搜索Mongoose,出现下面的界面,并安装。

    2.建立连接

    到这一步环境终于是准备好了,接下来在app_server 文件夹下创建一个models目录,并新建一个db.js

    并在db.js中引用Mongoose:

    var mongoose = require('mongoose');

    mongodb不需要在连接它之前创建数据库,当第一次连接的时候,会根据链接自动创建。Mongoose在连接MongoDB的时候会创建一个有五个可重用的连接的连接池,连接数是可以配置的。这么做的原因是因为连接数据库是比较耗时的操作,特别是分布式的数据库。连接字符串的规则如下:

    这里用户名、密码、端口都是可以省略的,那么在本地的时候我们的连接字符串就如下:

    var dbURI = 'mongodb://localhost/RClub';
    mongoose.connect(dbURI);

    命名我们的数据库为RClub。Mongoose会基于连接的状态发布不同的事件:

    mongoose.connection.on('connected', function () {
        console.log('Mongoose connected to ' + dbURI);
    });
    mongoose.connection.on('error', function (err) {
        console.log('Mongoose connection error: ' + err);
    });
    mongoose.connection.on('disconnected', function () {
        console.log('Mongoose disconnected');
    });

    如此我们可以监听连接的状态。另外应用终止需要监听nodejs的进程的SIGINT事件。而如果是nodemon重启,需要监听的是SIGUSR2事件,以便关闭连接。

    // 当应用重启或终止的时候 关闭连接
    Shutdown = function (msg, callback) {
        mongoose.connection.close(function () {
            console.log('Mongoose disconnected through ' + msg);
            callback();
        });
    };
    
    // nodemon 重启 
    process.once('SIGUSR2', function () {
        Shutdown('nodemon restart', function () {
            process.kill(process.pid, 'SIGUSR2');
        });
    });
    
    // 应用终止
    process.on('SIGINT', function () {
        Shutdown('app termination', function () {
            process.exit(0);
        });
    });
    View Code

    运行nodemon:

    显示连接成功!

    3.数据模型

    Mongoose封装了mongodb的api使之更便于调用,但作为MongoDB的对象-文档建模器,有着更强大的功能。在mongodb中,每一个条目就是一个document,相当于关系型数据库中的行,而document的集合称为collection,相当于是关系型数据库中的table。在Mongoose中定义一个document的对象称为schema,相当于用EF定义一个Model,而定义schema中每一条数据的规则称为path(约束)。path的规则和jquery.validate.js几乎是一样的。

    path支持的数据类型如下:

    • String
    • Number
    • Date
    • Boolean
    • Buffer   二进制,比如图形
    • Mixed  任何类型
    • Array  可以为数组,或者内嵌的 子文档集
    • ObjectId 唯一id

    接下来演示下如何用Mongoose建一个数据模型,在models文件夹下创建一个books.js。然后引用Mongoose。如下,更具上一节book对象创建一个bookSchema 。

      {
    id: 0, title: "深入浅出Node.js", info: "朴灵 / 人民邮电出版社 / 2013-12-1 / CNY 69.00", rating: 5, img: "https://img3.doubanio.com/mpic/s27269296.jpg", tags: ["node", "深入浅出"], brief: '本书从不同的视角介绍了 Node 内在的特点和结构。..... ,ISBN: 9787115335500 }

    id不用创建,mongo会为每个document生成一个唯一的id。

    var mongoose = require('mongoose');
    
    var bookSchema = new mongoose.Schema({
        title: String,
        rating: {
            type: Number,
            required: true,
            min: 0,
            max: 5
        },
        info: String,
        img: String,
        tags: [String],
        brief: String,
        ISBN: String
    });

    这种语法是不是和jquery.validate.js很像,相信不用解释就能看明白,不满足path的条件时是不能保存或者更改的。每个path还可以设置默认值,比如时间:

     createdOn: {
            type: Date,
            default: Date.now
        },

    一个Schema可以包含另外的Schema或数组,在关系型数据库中这种需求用的是外键或者是关系映射表,而Mongoose这样看起来直观多了。

    var userSchema = new mongoose.Schema({
    userName: String,
        email: String,
    ...});
    var commentSchema = new mongoose.Schema({
        user: userSchema,
        ...
        content: String
    });
    var topicSchema = new mongoose.Schema({
        ....
        visitedCount: { type: Number, default: 0 },
        ...
        comments: [commentSchema],
        deleted: { type: Boolean, default: false },
        top: { type: Boolean, default: false }, // 置顶帖
        ...
    });

    这个时候的shema 还不具备数据库的操作能力,还需要注册下。 

    mongoose.model('Book', bookSchema);
    mongoose.model('Topic', topicSchema);

    全部模型:

    var mongoose = require('mongoose');
    
    var bookSchema = new mongoose.Schema({
        title: String,
        rating: {
            type: Number,
            required: true,
            min: 0,
            max: 5
        },
        info: String,
        img: String,
        tags: [String],
        brief: String,
        ISBN: String
    });
    
    var userSchema = new mongoose.Schema({
        userName: String,
        email: String,
        password: String,
        createdOn: {
            type: Date,
            default: Date.now
        },
        img: String,
        ip: String,
        mobile: String
    });
    
    var commentSchema = new mongoose.Schema({
        user: userSchema,
        createdOn: {
            type: Date,
            default: Date.now
        },
        content: String
    });
    var topicSchema = new mongoose.Schema({
        title: String,
        type: String,
        visitedCount: { type: Number, default: 0 },
        commentCount: { type: Number, default: 0 },
        createdOn: {
            type: Date,
            default: Date.now
        },
        img: String,
        author: String,
        content: String,
        comments: [commentSchema],
        deleted: { type: Boolean, default: false },
        top: { type: Boolean, default: false }, // 置顶帖
        good: { type: Boolean, default: false }, // 精华帖
    });
    
    
    
    mongoose.model('Book', bookSchema);
    mongoose.model('Topic', topicSchema);
    View Code

    4.操作数据

    接下来就是改造controllers文件夹的中的home.js了,首先要引用db.js和Mongoose。

    var db = require('../models/db.js');
    var mongoose = require('mongoose');

     1).初始化数据,查询与新建数据。

     这里把上一节创建的数据books和topics存入数据库中:

    module.exports.init = function (req, res) {
        mongoose.model('Book').find().exec(function (err, objs) {
            if (err) {
                res.render('error', {
                    message: err.message,
                    error: err
                });
                return;
            }
            if (objs.length) {
                
                for (var i = 0; i < books.length; i++) {
                    var taget = new Bookmodel(books[i]);//创建一个模型
                    taget.save(function (err) {//保存。
                        console.log(err);
                    });
                }
                
                for (var i = 0; i < topics.length; i++) {
                    var taget = new topicmodel(topics[i]);
                    console.log('topic create');
                    taget.save(function (err) {
                        console.log(err);
                    });
                }
                res.send('初始化完成...');
            }
            res.render('books', { title: 'Books', books: objs });
        });

    这段代码里面有查询和新建保存,mongoose.model("Book") 相当于EF中的db.Books。而这里的find()方法并没有立即执行,这里有点像Linq的感觉,可以链式查询:

    Book.find({ size: 'small' }).where('createdDate').gt(oneYearAgo).exec(callback);//例子,和上面的book没有关系

    这里Book相当于mongoose.model("Book") ,而{ size: 'small' }是一个条件。最终到exec方法时才正在执行。回调函数默认有两个参数,一个是err,一个是结果对象。这里偷了懒只判断了books的查询结果,集合为空的话就加上原来的数据。因为mongoose.model("Book")使用的比较多,所以干脆提出来:

    var Bookmodel = mongoose.model('Book');
    var Topicmodel = mongoose.model('Topic');

    find方法是查询一个集合,常用的还有findone:

    var query = Person.findOne({ 'name.last': 'Ghost' });
    // 选择name和occupation 字段
    query.select('name occupation');
    
    // 执行查询
    query.exec(function (err, person) {
      if (err) return handleError(err);
      console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) 
    })

    保存数据,new一个Model,传入初始化的对象,save即可。

     var taget = new Bookmodel(books[i]);
          taget.save(function (err) {
                   console.log(err);
              });

    配置下路由:

    router.get('/init', homeController.init);

    运行:

    因此数据添加成功!

    三、app_api

    为方便后面复用,接下来将数据调用换成api的方式。在工程里面新建一个app_api文件夹,并将原来app_server目录下的models文件移过去。并增加controllers和routes文件夹。也就是说,把数据调用分离出来。

    1)在app.js中增加以下代码:

    var routesApi = require('./app_api/routes/index');
    ...
    app.use('/', routes);
    app.use('/api', routesApi);

    也就是说,所有以/api/xx开始的请求将由routes/index.js来路由。

    2)在routes文件下新增index.js 文件,用来配置api的路由:

    var express = require('express');
    var router = express.Router();
    var bookCtrl = require('../controllers/book');
    var topicCtrl = require('../controllers/topic');
    
    router.get('/books', bookCtrl.books);
    router.post('/book', bookCtrl.bookCreate);
    router.get('/book/:bookid', bookCtrl.bookReadOne);
    router.put('/books/:bookid', bookCtrl.bookUpdateOne);
    router.delete('/books/:bookid', bookCtrl.bookDeleteOne);
    
    //topics
    router.get('/topics', topicCtrl.topics);
    
    module.exports = router;

    路由覆盖了增删改查,暂时先实现这部分的路由。后面根据需求再增加。

    3)controller实现

    一个成功的Restful API应该包含返回的数据和http的状态码,因此我们定义了一个公用方法:

    var sendJSONresponse = function (res, status, content) {
        res.status(status);
        res.json(content);
    };

    比如定义一个bookReadOne方法:

    module.exports.bookReadOne = function (req, res) {
        var bookid = req.params.bookid;
        if (!bookid) {
            sendJSONresponse(res, 404, {
                "message": "Not found, bookid is required"
            });
            return;
        }
        BookModel.findById(bookid).exec(function (err, book) {
            if (!book) {
                sendJSONresponse(res, 404, {
                    "message": "bookid not found"
                });
                return;
            } else if (err) {
                sendJSONresponse(res, 400, err);
                return;
            }
            console.log(book);
            sendJSONresponse(res, 200, book);
    
        });
    }

    请求成功发送状态码200和结果,请求失败则发送404或者500及错误。

    books.js:

    var mongoose = require('mongoose');
    var BookModel = mongoose.model('Book');
    
    var sendJSONresponse = function (res, status, content) {
        res.status(status);
        res.json(content);
    };
     
    
    module.exports.books = function (req, res) {
        BookModel.find().exec(function (err, books) {
            if (err) {
                console.log(err);
                sendJSONresponse(res, 400, err);
                return;
            }
            sendJSONresponse(res, 200, books);
        });
    }
    
    module.exports.bookCreate = function (req, res) {
        BookModel.create({
            title: req.body.title,
            info: req.body.info,
            img: req.body.img,
            tags: req.body.tags,
            brief: req.body.brief,
            ISBN: req.body.ISBN
        }, function(err, book) {
            if (err) {
                console.log(err);
                sendJSONresponse(res, 400, err);
            } else {
                console.log(book);
                sendJSONresponse(res, 201, book);
            }
        });
    }
    
    module.exports.bookReadOne = function (req, res) {
        var bookid = req.params.bookid;
        if (!bookid) {
            sendJSONresponse(res, 404, {
                "message": "Not found, bookid is required"
            });
            return;
        }
        BookModel.findById(bookid).exec(function (err, book) {
            if (!book) {
                sendJSONresponse(res, 404, {
                    "message": "bookid not found"
                });
                return;
            } else if (err) {
                sendJSONresponse(res, 400, err);
                return;
            }
            console.log(book);
            sendJSONresponse(res, 200, book);
    
        });
    }
    
    module.exports.bookUpdateOne = function (req, res) {
        var bookid = req.params.bookid;
        if (!bookid) {
            sendJSONresponse(res, 404, {
                "message": "Not found, bookid is required"
            });
            return;
        }
        BookModel.findById(bookid).exec(function (err, book) {
            if (!book) {
                sendJSONresponse(res, 404, {
                    "message": "bookid not found"
                });
                return;
            } else if (err) {
                sendJSONresponse(res, 400, err);
                return;
            }
            book.title = req.body.title;
            book.rating = req.body.rating;
            book.info = req.body.info;
            book.img = req.body.img;
            book.tags = req.body.tags;
            book.brief = req.body.brief;
            book.ISBN = req.body.ISBN;
            book.save(function (err, book) {
                if (err) {
                    sendJSONresponse(res, 404, err);
                } else {
                    sendJSONresponse(res, 200, book);
                }
            });
        });
    
    
    }
    
    module.exports.bookDeleteOne = function (req, res) {
        var bookid = req.params.bookid;
        if (bookid) {
            BookModel.findByIdAndRemove(bookid)
                .exec(function (err) {
                if (err) {
                    console.log(err);
                    sendJSONresponse(res, 404, err);
                    return;
                }
                console.log("book id :" + bookid + "deleted");
                sendJSONresponse(res, 204, null);
            });
        } else {
            sendJSONresponse(res, 404, { message: "No bookid" });
        }
    }
    View Code

    这样注意一点是,find()、findbyid和findByIdAndRemove不是立即执行,是在exec方法中执行,但是create,save是立即执行的。

    5)、调用api

    接下来就是在app_server下的controller中调用上面定义的api。在这里需要安装一个request模块,request让http请求变的更加简单。

     在home.js中引用request:

    var request = require('request');

    request的调用如下:

    request(options, callback)

    包含options和回调两部分,options结构如下,包含url,method,json和qs四部分。

    var requestOptions = {
    url : "http://yourapi.com/api/path",
    method : "GET",
    json : {}, //请求体的JavaScript对象
    qs : {
    offset : 20 //查询字符串
    }
    };

    而默认的回调格式如下:

    function(err, response, body) {
    if (err) {
    console.log(err);
    } else if (response.statusCode === 200) {
    console.log(body);
    } else {
    console.log(response.statusCode);
    }
    }

    修改index方法,先定义一个apiOptions,只想默认路径,记得在发布的时候修改为带域名的地址。

    var apiOptions = {
        server : "http://localhost:3000"
    };
    
    module.exports.index = function (req, res) {
        var requestOptions, path;
        path = "/api/topics";
        requestOptions= {
            url: apiOptions.server + path,
            method: "GET",
            json:{},
        }
        request(requestOptions, function (err, response, body) {
            if (response.statusCode == 200) {
                res.render('index', { title: 'Index', topics: body });
            } else {
                res.render('error', { message: err.message, error: err });
            }
        });
    };

    这样就进行了分离,home.js不在需要引用Mongoose部分。

    home.js:

    var request = require('request');
    
    var apiOptions = {
        server : "http://localhost:3000"
    };
    //if (process.env.NODE_ENV === 'production') {
    //    apiOptions.server = "https://stoneniqiu-mean.herokuapp.com/ ";
    //}
    
    
    module.exports.index = function (req, res) {
        var requestOptions, path;
        path = "/api/topics";
        requestOptions= {
            url: apiOptions.server + path,
            method: "GET",
            json:{},
        }
        request(requestOptions, function (err, response, body) {
            if (response.statusCode == 200) {
                res.render('index', { title: 'Index', topics: body });
            } else {
                res.render('error', { message: err.message, error: err });
            }
        });
    };
    
    module.exports.books = function (req, res) {
        var requestOptions, path;
        path = "/api/books";
        requestOptions = {
            url: apiOptions.server + path,
            method: "GET",
            json: {},
        }
        request(requestOptions, function (err, response, body) {
            if (response.statusCode == 200) {
                res.render('books', { title: 'Books', books: body });
            } else {
                res.render('error', { message: err.message, error: err });
            }
        });
    };
    
    
    
    module.exports.detail = function (req, res) {
        var requestOptions, path;
        path = "/api/book/" + req.params.id;
        requestOptions = {
            url: apiOptions.server+path ,
            method: "GET",
            json: {},
        }
        request(requestOptions, function (err, response, body) {
            if (response.statusCode == 200) {
                res.render('detail', { title: body.title, book: body });
            } else {
                res.render('info', err);
            }
        });
       
       };
    
    module.exports.about = function (req, res) {
        res.render('about', { title: 'About' });
    };
    
    
    
     
    View Code   

    更多查询可以移步:http://mongoosejs.com/docs/queries.html

    在首页,需要处理一下时间显示的问题,增加了一个mixin

    mixin formatDate(dateString)
      -var date = new Date(dateString);
      -var d = date.getDate();
      -var monthNames = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ];
      -var m = monthNames[date.getMonth()];
      -var y = date.getFullYear();
      -var output = y + '/' + m + '/' + d;
      =output

    调用:

       span.pull-right
               +formatDate(topic.createdOn)

    页面如下:

    初步改造完成。之前还写了一个基于回调的service方式的controller和service,类似于C#中的分离。后来觉得还是api的方式更好,留在这里可以当一个参考。

    home.js:

    var bookservice = require('../services/homeService.js');
     
    module.exports.index = function (req, res) {
        bookservice.allTopics(function (result) {
            var obj = result.content;
            console.log(obj.status);
            if (result.status == 200) {
                res.render('index', { title: 'Index', topics: obj });
            }
            res.render('info', obj);
        });  
    };
    
    module.exports.books = function (req, res) {
        bookservice.allBooks(function(result) {
            var obj = result.content;
            console.log(obj.status);
            if (result.status == 200) {
                res.render('books', { title: 'Books', books: obj });
            }
            res.render('info', obj);
        });  
    };
    
    
    
    module.exports.detail = function (req, res) {
        bookservice.bookReadOne(req.params.id, function (result) {
            var obj = result.content;
            if (result.status == 200) {
                res.render('detail', { title: obj.title, book: obj });
            } 
            res.render('info', obj);
        });
       };
    
    module.exports.about = function (req, res) {
        res.render('about', { title: 'About' });
    };
    
    
    
     
    View Code

    homeService.js:

    var mongoose = require('mongoose');
    var db = require('../models/db.js');
    var Bookmodel = mongoose.model('Book');
    var Topicmodel = mongoose.model('Topic');
    
    
    var jsonResult = function (status, content) {
        return { status: status, content: content };
    };
    
    module.exports.bookReadOne = function (id,callback) {
        console.log('Finding book details', id);
        if (!id) {
            console.log('No bookid specified');
            return jsonResult(404, { "message": "No bookid specified" });
        }
        Bookmodel.findById(id).exec(function (err, book) {
            if (err) {
                callback(jsonResult(404, err));
                return;
            }
            if (!book) {
                callback(jsonResult(404, { "message": "book not found" }));
                return;
            }
           callback(jsonResult(200, book));
        });
    
    };
    module.exports.allBooks=function(callback) {
        Bookmodel.find().exec(function (err, books) {
            if (err) {
                callback(jsonResult(404, err));
                return;
            }
            if (!books.length) {
                callback(jsonResult(404, { "message": "books not found" }));
                return;
            }
            callback(jsonResult(200, books));
        });
    }
    module.exports.createBook = function (book, callback) {
        var t = new Bookmodel(book);
        t.save(function (err) {
            callback(err);
        });
    }
    
    module.exports.allTopics=function(callback) {
        Topicmodel.find().exec(function (err, topics) {
            if (err) {
                callback(jsonResult(404, err));
                return;
            }
            if (!topics.length) {
                callback(jsonResult(404, { "message": "topics not found" }));
                return;
            }
            callback(jsonResult(200, topics));
        });
    }
    
    module.exports.createTopic=function(topic, callback) {
        var t = new Topicmodel(topic);
        t.save(function(err) {
            callback(err);
        });
    }
    
    
    var books = [
        {
            id: 0,
            title: "深入浅出Node.js",
            info: "朴灵 / 人民邮电出版社 / 2013-12-1 / CNY 69.00",
            rating: 5,
            img: "https://img3.doubanio.com/mpic/s27269296.jpg",
            tags: ["node", "深入浅出"],
            brief: '本书从不同的视角介绍了 Node 内在的特点和结构。由首章Node 介绍为索引,涉及Node 的各个方面,主要内容包含模块机制的揭示、异步I/O 实现原理的展现、异步编程的探讨、内存控制的介绍、二进制数据Buffer 的细节、Node 中的网络编程基础、Node 中的Web 开发、进程间的消息传递、Node 测试以及通过Node 构建产品需要的注意事项。最后的附录介绍了Node 的安装、调试、编码规范和NPM 仓库等事宜。本书适合想深入了解 Node 的人员阅读。'
            ,ISBN: 9787115335500
        },
        {
            id: 1,
            title: "程序员修炼之道 : 从小工到专家",
            info: "Andrew Hunt、David Thomas / 马维达 / 电子工业出版社 / 2005-1 / 48.00元",
            rating: 5,
            img: "https://img3.doubanio.com/mpic/s3957863.jpg",
            tags: ["程序人生", "软件开发"],
            brief: '《程序员修炼之道》由一系列的独立的部分组成,涵盖的主题从个人责任、职业发展,直到用于使代码保持灵活、并且易于改编和复用的各种架构技术。利用许多富有娱乐性的奇闻轶事、有思想性的例子以及有趣的类比,全面阐释了软件开发的许多不同方面的最佳实践和重大陷阱。无论你是初学者,是有经验的程序员,还是软件项目经理,本书都适合你阅读。'
            ,ISBN: 9787505397194
        },
        {
            id: 2,
            title: "Getting MEAN with Mongo, Express, Angular, and Node",
            info: "Simon Holmes / Manning Publications / 2015-11-26 / USD 44.99",
            rating: 4,
            img: "https://img3.doubanio.com/mpic/s27676844.jpg",
            tags: ["node", "web开发", "编程"],
            brief: 'MEAN栈开发,比较详尽的的应用开发书籍'
            , ISBN: 9781617292033
        }
    ];
    var topics = [
        {
            title: "书山有路第十一期:程序员修炼之道-第二章-注重实效的途径--第五天",
            type: "读书",
            visitedCount: 80,
            commentCount: 2,
            createdOn: '2016/5/15 21:32',
            author: 'stoneniqiu',
            img: 'http://upload.jianshu.io/users/upload_avatars/133630/d5370e672fd4.png?imageMogr/thumbnail/90x90/quality/100'
        },
        {
            title: "《明朝那些事儿》之闲言散语",
            type: "书评",
            visitedCount: 180,
            commentCount: 20,
            createdOn: '2016/5/15 21:32',
            author: '卡卡卡萨布兰卡 ',
            img: 'http://upload.jianshu.io/users/upload_avatars/1675188/2d0810ccc03d.jpg?imageMogr/thumbnail/90x90/quality/100'
        },
        {
            title: "有《程序员修炼之道》高清版吗?",
            type: "求书",
            visitedCount: 90,
            commentCount: 1,
            createdOn: '2016/5/15 21:32',
            author: '吾不知 ',
            img: 'http://upload.jianshu.io/users/upload_avatars/1125491/3910f3825f73.jpg?imageMogr/thumbnail/90x90/quality/100',
        },
        {
            title: "《国富论》-读书笔记",
            type: "书评",
            visitedCount: 180,
            commentCount: 20,
            createdOn: '2016/5/15 21:32',
            author: '寻海 '
            ,img: 'http://upload.jianshu.io/users/upload_avatars/133630/d5370e672fd4.png?imageMogr/thumbnail/90x90/quality/100'
        },
        {
            title: "《高效人士的七个习惯》读书笔记",
            type: "书评",
            visitedCount: 180,
            commentCount: 20,
            createdOn: '2016/5/15 21:32',
            author: '书虫纪庆 ',
            img: 'http://upload.jianshu.io/users/upload_avatars/1429280/454c495361f9.jpg?imageMogr/thumbnail/90x90/quality/100'
        },
        {
            title: "《css揭秘》这本书如何",
            type: "求索",
            visitedCount: 58,
            commentCount: 3,
            createdOn: '2016/5/15 21:32',
            author: 'Watery_D_Lotus ',
            img: 'http://upload.jianshu.io/users/upload_avatars/1449533/a2d98762484a.jpg?imageMogr/thumbnail/90x90/quality/100'
        }
    ];
    
    
    
    //module.exports.bookCreate = function (req, res) {
    //    var book = {
    //        title: "test",
    //        info: "Simon Holmes / Manning Publications / 2015-11-26 / USD 44.99",
    //        rating: 4,
    //        img: "https://img3.doubanio.com/mpic/s27676844.jpg",
    //        tags: ["node", "web开发", "编程"],
    //        brief: 'MEAN栈开发,比较详尽的的应用开发书籍',
    //        ISBN: 9781617292033
    //    };
    //    var b = new Bookmodel(book);
    //    b.save(function (err) {
    //        if (err) {
    //            console.log(err);
    //            res.render('error', {
    //                message: err.message,
    //                error: err
    //            });
    //        }
    //        res.render('info', { message: "添加成功!", success: true });
    //    });
    
    //};
    //module.exports.bookDelete = function (req, res) {
    //    Bookmodel.findOneAndRemove({ title: "test" }, function (err) {
    //        if (err) {
    //            console.log(err);
    //            res.render('error', {
    //                message: err.message,
    //                error: err
    //            });
    //        }
    //        res.render('info', { message: "删除成功!", success: true });
    //    });
    //};
    View Code

    至此,这三个页面的数据全部都是通过api从数据库中获取。那么现在我们要发布,本地数据库显然是不行。那怎么办呢?

    四、mlab,500M MongoDB免费空间!

     mlab是一个提供部署在云端的mongodb数据库服务平台,可以对每个用户免费提供500M空间。

     1. 先注册一个账号:https://mlab.com/,并验证邮箱:

     2.点击Create new创建数据库:这里选择亚马逊的单用户sanbox会有免费的500M空间。

     输入数据库名称,并创建

    3.创建成功之后,给数据库创建一个用户

     4.连接字符串: 

     第一个字符串是用cmd连接,第二个用于我们的程序。修改db.js 连接成功:

    但记得初始化数据。到这里这一节就基本结束了。更多细节可以参考:

    源码:https://github.com/stoneniqiu/ReadingClub/tree/mongoose

    访问地址:https://stoneniqiu-mean.herokuapp.com/

    小结:这一节篇幅有点长,整理了五六个小时,主要涉及了mongodb的基本操作和安装服务相关;Mongoose的建模以及使用Mongoose进行增删改查;以及api的封装和调用;最后介绍了mlab,来把mongodb部署到云端。这样我们继续可以使用heroku发布。文中的操作所覆盖的api操作可能不全面,这个大家可以去根据需要去参考官方文档。下一节讲解form提交、验证以及文件上传。

  • 相关阅读:
    selenium实战
    selenium+unittest实现po模型
    IDE自动化脚本实现
    web 自动化测试
    requests 遇到报错UnicodeEncodeError: 'latin-1' codec can't encode character 'u86c7' in position 8: Body ('蛇') is not valid Latin-1. Use body.encode('utf-8') if you want to send it encoded in UTF-8.
    通过requests + pytest + allure 实现接口自动化测试
    python pytest单元测试
    python unittest单元测试
    laili和网路知识
    网络-单臂路由
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/5556669.html
Copyright © 2020-2023  润新知