MongoDB
数据库概述和环境搭建,MongoDB 增删查改操作,模板引擎
第一部分
1, 数据库概述和环境搭建
1.1 为什么要使用数据库
动态网站的数据存储在数据库中 http://www.czxy.com/artical?id=1 http://www.czxy.com/artical?id=2id不一样页面不一样
数据库用来持久存储客户端通过表单手机的用户信息
数据库软件本身 可以对数据进行高效的管理:商品信息,用户信息分类存储,快速查找的目的;
1.2 什么数据库
数据库就是 存储数据的仓库,可以将数据进行有序的分门别类的存储,对数据进行高效的管理。他是独立于语言之外的 软件,通过API操作 Node.js ß--(数据库提供的API,数据库反馈操作结果)---à 数据库
常见的数据库软件:mysql(php), mongoDB, oracle;
选择MongoDB, 开发的API 采用的也是JS 语法,和nodejs 一样,存数据的方式是 json 对象格式的,学习更加容易,更加友好
1.3 下载安装
MongoDB: https://www.mongodb.com/try/download/community
MongDB compass: https://www.mongodb.com/try/download/compass
1.4 MongoDB 可视化软件
compass 可以通过图形界面的方式操作数据库
nodejs 为数据库提供 API; 数据库反馈操作结果 到 Nodejs
compass 通过界面操作数据库; 数据库通过界面反馈操作结果
1.5 数据库
在一个数据库软件中,可以包含多个 数据仓库,在每个数据仓库中可以包含多个数据集合,每个数据集合可以包含多个文档(具体的数据)
1.6 MongoDB第三方模块
使用node.js 操作MongoDB数据库需要依赖node,js的第三方包mongoose
npm install mongoose
1.7 启动 mongoDB
我们需要开启服务:打开数据库软件,才能连接;默仁启动的,当电脑重新开机,关闭了如何开启?
命令行工具:net start mongoDB / net stop mongodb
1.8 数据库连接
服务启动以后,如何连接数据库:connect() 方法,穿得连接数据库的地址和名字,在建立网站使用的协议是 HTTP,数据库使用的协议是 mongodb 协议;由于是安装到自己电脑上,所以用 localhost 访问;如果localhost后数据库名字没有,可以随便写;由于 mongoose.connect() 方法返回的是一个 promise 对象,所以就可以用.then() .catch()
| 01.js // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true}) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed')); |
| 在命令行:node 01.js (node:5004) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor. 出现提示: useUnifiedTopology: true 复制粘贴到 mongoose.connect()方法的第二个参数里 |
| 【问题】 const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost1/playground', { useNewUrlParser: true}) .then(() => console.log('Database connection successful')) .catch(err => console.log(err, 'Database connection failed')); 连接://localhost1/错误,会走.catch()方法 |
1.9 创建数据库
不用显式的去创建数据库,如果正在使用的不存在,会自动帮你创建
3. MongoDB 增删查改操作
3.1 创建集合
创建集合分两步:
第一,先创建集合规则:在这个集合当中的文档,要拥有那些字段,字段的类型是啥,是字符串,数值,布尔值?第二,使用规则,创建集合
1)创建集合规则 mongoose.Schema 创建集合规则就是创建构造函数schema的实例对象
2)创建集合 Model()方法 mongoose 规定,集合名称首字母大写;他创建之后的回编程小写并加上 s表示复数;这个方法返回一个 构造函数,代表当前集合;可以使用这个构造函数的方法,进行各种操作
Model()方法中的参数:
- 第一个参数:‘集合名称首字母大写‘,- 他自己在数据库创建的是小写而且是复数
- 第二个参数: 集合的规则
3.2 创建文档 - 向集合插入数据
3.2.1 方法一: 创建集合实例
- // 连接数据库:命令行进行 node 02.js
- // 虽然连接了数据库,创建了集合,但是没有插入数据,自然不会创建数据库和集合
- // 创建文档
- 创建集合实例
- 调用实例对象下的 save() 方法保存到数据库中
| 02.js 如何创建集合以及向集合中插入文档 |
| // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/01test', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed'));
// 创建集合规则 = 创建schema的实例对象 const courseSchema = new mongoose.Schema({ name: String, // 课程名字的类型 author: String, // 课程作者类型 isPublished: Boolean // 课程是否发布;课程的一个状态,是布尔值 });
// 使用规则创建集合model()方法:参数有2 // 1.集合名称:首字母大写,在数据库中起的的名字courses // 2.集合规则:刚才创建的集合规则 // 返回构造函数,用一个变量接收它 const Course = mongoose.model('Course', courseSchema) // courses
// 连接数据库:命令行进行 node 02.js // 虽然连接了数据库,创建了集合,但是没有插入数据,自然不会创建数据库和集合 // 创建文档 const course = new Course({ name: 'node.js1', author: 'luojin1', isPublished: true });
// 将文档插入到数据库中 course.save(); |
3.2.2 方法二:构造函数的 create() 方法
第二个参数回调函数,说明当前API 是异步操作;关于数据库的所有操作都是异步的;在mongoose 中,所提供的API,支持两种获取API 的结果;一种就是 回到函数方式; 另一种也支持 promise 方式;
参数一:对象类型的文档
参数二:回调函数;插入以后,系统调用这个回调函数,错误对象
Create也返回 Promise对象
| 03.js 向集合中插入文档的另一种方式 |
| // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/01test', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed'));
// 创建集合规则 const courseSchema = new mongoose.Schema({ name: String, author: String, isPublished: Boolean });
// 使用规则创建集合 // 1.集合名称 // 2.集合规则 const Course = mongoose.model('Course', courseSchema) // courses
// 向集合中插入文档 // Course.create({name: 'Javascript', author: 'luojin', isPublished: false}, (err, result) => { // console.log(err) // console.log(result) // }) // 回调函数 说明了当前API 关于数据库的操作都是异步的;
// promise 方式; Course.create({name: 'Javascript123', author: '黑马讲师', isPublished: false}) .then(result => { console.log(result) }) |
3.3 mongoDB 数据库导入数据
插入现成的数据;
- 命令:mongoimport -d 数据库名称 -c 集合名称 -file 要导入的数据
- 但是我们不能使用现在,因为命令行去计算机查找mongimport的可执行文件,需要手动将命令添加到系统环境变量中;
- 找到 mongoose的可执行文件目录;复制添加到环境变量中;
mongoimport -d 01test -c users --file ./user.json
3.4 查询文档
1) find() 方法也返回promise对象
//3, 查询用户集合中的所有文档
//User.find().then(result => console.log(result));
//3.1 通过id字段查找
//User.find({_id: '5c09f267aeb04b22f8460968'}).then(result => console.log(result));
//4,返回一个,默认返回第一条
//User.findOne().then(result => console.log(result));
//4.1 可以加条件
User.findOne({name: '李四'}).then(result => console.log(result));
- 匹配大于,小于
User.find({age: {$gt: 20, $lt: 50}}).then(result => console.log(result))
- 匹配包含 $in 网站搜索时,在后台查询的时候用
User.find({hobbies:{$in:['敲代码']}}).then(result => console.log(result))
- 选择要查询的字段
User.find().select('name email').then(result => console.log(result));
- 根据年龄进行升序排列
User.find().sort('age').then(result => console.log(result));
- 根据年龄进行降序排列
User.find().sort('-age').then(result => console.log(result));
- Skip 跳过多少条数据,limit限制查询数量 = 翻页使用;跳过前两个文档,只显示3个数
User.find().skip(2).limit(3).then(result => console.log(result));
| 04.js 文档查询 |
| // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/01test', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed'));
// 创建集合规则 const userSchema = new mongoose.Schema({ name: String, age: Number, email: String, password: String, hobbies: [String] });
// 使用规则创建集合 const User = mongoose.model('User', userSchema);
// 查询用户集合中的所有文档 // User.find().then(result => console.log(result)); // 通过_id字段查找文档 // User.find({_id: '5c09f267aeb04b22f8460968'}).then(result => console.log(result))
// findOne方法返回一条文档 默认返回当前集合中的第一条文档 // User.findOne({name: '李四'}).then(result => console.log(result))
// 查询用户集合中年龄字段大于20并且小于40的文档 // User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result))
// 查询用户集合中hobbies字段值包含足球的文档 // User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result))
// 选择要查询的字段 -_id': 加一个 - 表示不想查询的字段 // User.find().select('name email -_id').then(result => console.log(result))
// 根据年龄字段进行升序排列 // User.find().sort('age').then(result => console.log(result)) // 根据年龄字段进行降序排列 // User.find().sort('-age').then(result => console.log(result)) // 查询文档跳过前两条结果 限制显示3条结果 User.find().skip(2).limit(3).then(result => console.log(result)) |
3.5 删除文档
1) 删除单个,也返回promise对象,通.then(); 查找到一条文档并删除,返回删除的文档,
User.findOneAndDelete({_id: '5c09f2d9aeb04b22f846096b'}).then(result => console.log(result));
2) 删除多个,不传全部删除,要小心, 参数也是有条件的
User.deleteMany({}).then(result => console.log(result));
3.6 更新文档
1) 更新文档
User.updateOne({查询条件},{要修改的值}).then(result => console.log(result));
User.updateOne({name:'李四'},{name: '李狗蛋'}).then(result=>console.log(result));
2) 更新多个
User.updateMany({查询条件},{要修改的值}).then(result => console.log(result));
User.updateMany({},{age: 56}).then(result=>console.log(result));
- mongoose验证
在创建集合规则时,可以设置当前字段的验证规则,验证失败就则插入失败
- Required: true 必传字段 , 不传入报错
- Min/max: 针对数值这样的字段类型
| 07.js |
| // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/01test', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed'));
const postSchema = new mongoose.Schema({ title: { type: String, // 必选字段 required: [true, '请传入文章标题'], // 字符串的最小长度 minlength: [2, '文章长度不能小于2'], // 字符串的最大长度 maxlength: [5, '文章长度最大不能超过5'], // 去除字符串两边的空格 trim: true }, age: { type: Number, // 数字的最小范围 min: 18, // 数字的最大范围 max: 100 }, publishDate: { type: Date, // 默认值 default: Date.now }, category: { type: String, // 枚举 列举出当前字段可以拥有的值 enum: { values: ['html', 'css', 'javascript', 'node.js'], message: '分类名称要在一定的范围内才可以' } }, author: { type: String, validate: { validator: v => { // 返回布尔值 // true 验证成功 // false 验证失败 // v 要验证的值 return v && v.length > 4 }, // 自定义错误信息 message: '传入的值不符合验证规则' } } }); //postSchema集合规则 使用它创建一个集合 const Post = mongoose.model('Post', postSchema);
Post.create({title:'aa', age: 60, category: 'java', author: 'bd'}) .then(result => console.log(result)) .catch(error => { // 获取错误信息对象 const err = error.errors; // 循环错误信息对象 for (var attr in err) { // 将错误信息打印到控制台中 console.log(err[attr]['message']); } }) |
- 集合关联(实现)
通常不同集合的数据之间是具有关系的,例如文章信息和用户信息存在不同的集合中,但文章是某个用户发表的,要查询文章的所有信息包括发表用户,就需要用到集合关联
- 使用id对集合进行关联:查找当前发布文章的作者信息
- 1, 创建用户集合规则 2,创建文章集合规则/数据库中创建首字母大写的用户集合和文章集合
- 2, 在文章集合的作者中,关联用户集合 type: mongoose.Schema.Types.ObjectId, ref: 'User'
- 3,创建用户
- 4, 创建一篇文章
- 5, 查询作者信息 Post.find().populate('author').then(result=>console.log(result))
| // 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/01test', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接成功 .then(() => console.log('Database connection successful')) // 连接失败 .catch(err => console.log(err, 'Database connection failed'));
// 用户集合规则 const userSchema = new mongoose.Schema({ name: { type: String, required: true } }); // 文章集合规则 const postSchema = new mongoose.Schema({ title: { type: String }, // 在文章集合中存储发布文章集合的作者;集合关联 author: { // 存储用户的下划线 id值 type: mongoose.Schema.Types.ObjectId, // 要关联的 user 集合 ref: 'User' } });
// 创建用户集合 const User = mongoose.model('User', userSchema); // 创建文章集合 const Post = mongoose.model('Post', postSchema);
// 创建用户 // User.create({name: 'itheima'}).then(result => console.log(result)); // 创建文章 // Post.create({titile: '123', author: '5c0caae2c4e4081c28439791'}).then(result => console.log(result)); Post.find().populate('author').then(result => console.log(result)) |
3.8 案例: 用户的增删改查
1)搭建网站服务器,实现客户端与服务器端的通信
2)连接数据库,创建用户集合,向集合中插入文档
3)当用户访问/list时,将所有的用户信息查询出来 : 路由 + 呈现用户页面
4)将用户访问/add时,呈现表单页面,并实现添加用户信息功能
6)当用户访问/modify时,呈现修改页面,并实现用户信息修改功能
7) 当用户访问/delete时,实现用户删除功能
第二部分
1, 模板引擎得基础概念
1.1 模板引擎
第三方模块。
让开发者以更加友好得方式拼接字符串,是项目代码更加清晰,更加易于维护
1.2 art-template模板引擎
1)种类很多,这个是由腾讯公司出品,目前运行最快的,公司使用最多的。
2)下载: npm install art-template
3) 使用:const template = require(‘art-template’) 引入;返回的是一个方法;将html 模板和数据进行拼接
4)调用它,就告诉模板引擎要拼接的数据和模板在哪 const html = template(“模板路径”,数据);第二个参数实际上是一个 对象类型,在模板中可以直接使用对象的属性来拿到对象的数据,显示到页面;在接下来,只需要模板引擎提供的特殊用法,告诉这个模板引擎,数据和模板要如何进行拼接
1.3 案例
// 调用 template 方法,做两件事:
//1,要将哪一个数据和哪一个模板进行拼接;
//2,告诉模板引擎 如何拼接?
// template方法是用来拼接字符串的,有两个参数
// 1. 模板路径 绝对路径 __dirname 当前文件所在目录
// 2. 要在模板中显示的数据 对象类型
// template() 返回拼接好的字符串
1) 后缀 .art
2. 模板引擎语法
2.1 模板语法
1) art-template 同时支持两种模板语法:标准语法,原始语法
2)标准语法:容易就阅读 {{数据}}
原始语法:强大的裸机处理能力 <%=数据 %>
2.2 输出
2.3 原文输出
1)如果数据中携带 HTML标签,默认模板引擎不会解析标签,会将其转义后输出
2)标准语法:{{ @ 数据 }}
3)原始语法:<%- 数据 %>
2.4 条件判断
1) 在模板中可以 根据条件决定 显示那块HTML代码?
| {{if age > 18}} 年龄大于18 {{else if age < 15 }} 年龄小于15 {{else}} 年龄不符合要求 {{/if}} |
| <% if (age > 18) { %> 年龄大于18 <% } else if (age < 15) { %> 年龄小于15 <% } else { %> 年龄不符合要求 <% } %> |
2.5 循环
1)从数据库中查询数据,会返回一个数组,包含多个对象,如何展示在页面中?通常采用循环的方式
2)标准语法:{{ each 数据}} {{/each}}
3) 原始语法: <% for() { %> <% } %>
| <ul> {{each users}} //开始循环 <li> //循环的标签 {{$value.name}} //要展示的数据 {{$value.age}} {{$value.sex}} </li> {{/each}} </ul> |
2.6 子模版
1)使用子模版 可以将 网站公共区块(头部,底部)抽离到单独的文件中去;单标记
2)标准语法:{{include ‘子模版的路径‘}} {{include ‘./header.art’}}
3)原始语法:<%include(‘子模版的路径‘) %> <%include(‘./header.art’) %>
2.7 模板继承
1) html 骨架在每个页面也属于公共部分
2)模板继承可以 将网站HTML骨架 抽离到单独的文件中,其他页面模板可以继承骨架文件
2.8 模板继承示例
{{block ‘ ’}}预留位置 {{/block}}
如何继承,如何填充;通过 extend
| {{extend './common/layout.art'}} // 继承layout模板
{{block 'content'}} // 填充模板中的预留的两个坑 <p>{{ msg }}</p> {{/block}}
{{block 'link'}} <link rel="stylesheet" type="text/css" href="./main.css"> {{/block}} |
2.9 模板配置
1) 向模板中导入变量 template.defaults.imports.变量名 = 变量值
- 从数据库中查时间,是原始的,不能直接显示,要处理格式化显示给用户;调用别人写好的方法;
- 日期处理的第三方模块: dateFormat()
- https://www.npmjs.com/package/dateformat
- 下载: $ npm install dateformat
- 导入: var dateFormat = require('dateformat');
- 使用:{{dateFormat(time, 'yyyy-mm-dd')}}
- new Date() 就是原始的时间
2)设置模板根目录 template.defaults.root = path.join(__dirname, 'views') 这样做的好处:多次重复的代码,消除
3)设置模板默认后缀
——————————————————————
3, 案例
3.1 案例介绍 – 学生档案管理
- 目标:模板引擎应用,强化node.js项目制作流程
- 知识点:http请求响应,数据库,模板引擎,静态资源访问
- 后期 优化代码
3.2 项目制作流程
1)建立项目文件夹 并 生成描述文件:package.json 文件,记录当前项目依赖的第三方模块和项目本身信息( 建立项目文件夹:students; 命令行切换到根目录:npm init -y 生成 package.json文件 )
2) 创建数据库服务器 实现客户端和服务器端通信:基础中的基础; 建立文件夹model 分离出创建服务器代码( students文件夹下,新建一个app.js: 用来创建网站服务器并实现客户端的请求和访问;命令行:nodemon app.js 启动服务器)
3)连接数据库 并 根据需求设计学院信息表: npm i mongoose
创建集合规则 user.js
创建集合 mongoose.model()
4)创建路由 并 实现页面模板呈递:当客户端向服务器端发送请求的时候,服务器要看一下客户端的请求路径,并根据这个路径实现一些功能和模板呈递
- npm install router
- 下载模板引擎 npm i art-template
5)实现静态资源访问: 发送请求的css, js, 图片文件等
6)实现学生信息添加功能
7)实现学生信息展示功能
3.3 第三方模块 router
- 功能:实现路由
- 使用步骤:1,获取路由对象
2,调用路由对象提供的方法创建路由
3,启用路由,使路由生效
-代码: npm install router
、
3.4 第三方模块 serve-static
- 功能:实现静态资源访问功能
- 使用步骤:
1,引入serve-static 模块获取创建静态资源服务功能的方法
2,调用方法创建静态资源服务并指定静态资源服务目录,当参数传递给他
3,启用静态资源服务功能:调用它
- 代码:npm install servestatic
3.5. 添加学生信息功能步骤分析
1)需要向服务器发请求的post,在模板的表单(action,method)中执行请求地址与请求方式
2)为每一个表单添加name属性
3)添加实现信息功能路由,处理请求
4)接收客户端请求来的学生信息
5)将学生信息太牛加到数据库
6)将页面重定向到学生信息页面
3.6 学生信息列表页面分析
1)将数据库中多有的学生信息查询出来
2)通过模板引擎将学生信息和HTML模板进行拼接
3)将拼接好的模板响应给客户端
- npm install dateformat 处理时间
model: 数据库相关代码
public: 静态资源
route: 路由
views: 模板
app.js: 入口文件,主文件