codecamp

Express Tutorial Part 3: Using a Database (with Mongoose)

先决条件: Express Tutorial Part 2: Creating a skeleton website
目的: 能够使用Mongoose设计和创建自己的模型。

概述

图书馆工作人员将使用当地图书馆网站存储有关图书和借阅者的信息,图书馆成员将使用它来浏览和搜索图书,查看是否有任何副本,然后预订或借阅。 为了有效地存储和检索信息,我们将其存储在数据库中。

Express应用可以使用许多不同的数据库,您可以使用多种方法来执行 C reate, R ead, U pdate和 > D 电子(CRUD)操作。 本教程简要概述了一些可用选项,然后详细显示所选的特定机制。

我可以使用什么数据库?

应用程序可以使用节点支持的任何数据( Express 本身没有定义数据库管理的任何特定附加行为/要求)。 许多常用选项,包括PostgreSQL,MySQL,Redis,SQLite和MongoDB

在选择数据库时,应考虑时间 - 生产力/学习曲线,性能,易于复制/备份,成本,社区支持等。虽然没有单一的"最佳"数据库,但几乎任何流行的解决方案 对于像我们的本地图书馆这样的中小型网站应该是可以接受的。

有关选项的详细信息,请参阅:数据库集成(Express docs)。

什么是与数据库交互的最佳方式?

有两种方法与数据库交互:

  • Using the databases' native query language (e.g. SQL)
  • Using an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.

使用SQL或数据库支持的任何查询语言都可以获得最佳的性能。 ODM通常较慢,因为它们使用翻译代码在对象和数据库格式之间进行映射,这可能不会使用最高效的数据库查询(如果ODM支持不同的数据库后端,则尤其如此,并且必须在什么数据库 支持功能)。

使用ORM的好处是程序员可以继续思考JavaScript对象而不是数据库语义 - 如果你需要使用不同的数据库(在相同或不同的网站上),这是特别真实的。 它们还提供了执行数据验证和检查的显而易见的地方。

提示:使用ODM / ORM通常会降低开发和维护成本! 除非您非常熟悉本机查询语言或性能至关重要,否则应该强烈考虑使用ODM。

我应该使用什么ORM / ODM?

NPM软件包管理器网站上提供了许多ODM / ORM解决方案(请查看 odm orm 标记的子集!)。

在写作时流行的一些解决方案是:

  • Mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
  • Waterline: An ORM extracted from the Express-based Sails web framework. It provides a uniform API for accessing numerous different databases, including Redis, mySQL, LDAP, MongoDB, and Postgres.
  • Bookshelf: Features both promise-based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. Works with PostgreSQL, MySQL, and SQLite3.
  • Objection: Makes it as easy as possible to use the full power of SQL and the underlying database engine (supports SQLite3, Postgres, and MySQL).

作为一般规则,在选择解决方案时,应考虑提供的功能和"社区活动"(下载,贡献,错误报告,文档质量等)。 在编写时Mongoose是目前最流行的ORM,如果你使用MongoDB为你的数据库是一个合理的选择。

使用Mongoose和MongoDb为LocalLibrary

对于 Local Library 示例(以及本主题的其余部分),我们将使用 "> Mongoose ODM 访问我们的库数据。 Mongoose充当 MongoDB 的前端,这是一个开源 href ="https://en.wikipedia.org/wiki/NoSQL"> NoSQL 数据库,它使用面向文档的数据模型。 "文档"的"集合",在MongoDB数据库中,类似 到关系数据库中的"行"的"表"。

这种ODM和数据库组合在Node社区中非常受欢迎,部分原因是文档存储和查询系统看起来非常像JSON,因此JavaScript开发人员很熟悉。

提示:您不需要知道MongoDB以使用Mongoose,但部分 "> Mongoose文档 更容易使用,并了解您是否已经熟悉MongoDB。

本教程的其余部分将介绍如何定义和访问 LocalLibrary网站示例的Mongoose模式和模型。

设计LocalLibrary模型

在你跳入并开始编码模型之前,值得花几分钟时间考虑我们需要存储什么数据以及不同对象之间的关系。

我们知道我们需要存储关于图书的信息(标题,摘要,作者,类型,ISBN),并且我们可能有多个副本可用(具有全球唯一的ID,可用性状态等)。 我们可能需要存储有关作者的更多信息,而不仅仅是他们的姓名,并且可能有多个作者使用相同或相似的名称。 我们希望能够根据书名,作者,类型和类别对信息进行排序。

在设计模型时,为每个"对象"(相关信息组)分别建立模型是有意义的。 在这种情况下,明显的对象是书,书实例和作者。

您可能还想使用模型来表示选择列表选项(例如,像下拉列表中的选项),而不是将选择硬编码到网站本身 - 这是建议当所有的选项都不知道前面或可能 更改。 这种类型的模型的明显候选是书类型(例如科幻小说,法国诗歌等)

一旦我们决定了我们的模型和领域,我们需要考虑它们之间的关系。

考虑到这一点,下面的UML关联图显示了我们在这种情况下定义的模型(如框)。 如上所述,我们已经创建了书的模型(书的通用细节),书实例(系统中可用的书的特定物理副本的状态)和作者。 我们还决定了一个类型模型,以便可以动态创建值。 我们决定不要为 BookInstance:status 建立一个模型 - 我们将硬编码可接受的值,因为我们不希望这些值发生变化。 在每个框中,您可以看到模型名称,字段名称和类型,以及方法及其返回类型。

该图还示出了模型之间的关系,包括它们的多重性。 多重性是图上的数字,其示出了可能存在于关系中的每个模型的数量(最大和最小)。 例如,框之间的连接线示出 Book Genre 是相关的。 靠近 Book 模型的数字表明一本图书必须有零个或多个 Genre (尽可能多),而下一行的另一端的数字 Genre 表示它可以有零个或多个关联的图书。

注意:如下面我们的 Mongoose入门中所述,通常最好是在一个模型中定义文档/模型之间的关系的字段 (您仍然可以通过搜索其他模型中相关的 _id 找到相反的关系)。 下面我们选择定义Book模式中的Book / Genre和Book / Author之间的关系,以及BookInstance模式中的Book / BookInstance之间的关系。 这个选择有点武断 - 我们可以同样有在另一个模式中的字段。

注意:下一节提供了一个基本的说明,说明如何定义和使用模型。 当你阅读它,考虑我们将如何构建上面的图中的每个模型。

Mongoose引物

本节提供如何将Mongoose连接到MongoDB数据库,如何定义模式和模型以及如何进行基本查询的概述。

请注意:此底稿受 Mongoose快速入门的影响很大 npm 官方文档

安装Mongoose和MongoDB

Mongoose安装在您的项目中( app.json ),就像使用NPM的任何其他依赖项一样。 要安装它,请在项目文件夹中使用以下命令:

npm install mongoose --save

安装 会添加所有的依赖项,包括MongoDB数据库驱动程序,但它不会安装MongoDB本身。 如果要安装MongoDB服务器,则可以从此处下载安装程序,以查看各种操作系统并安装 它在本地。 您还可以使用基于云的MongoDB实例。

注意:在本教程中,我们将使用基于云端的mLab数据库作为服务 / plans / pricing /"> sandbox tier 来提供数据库。 这适合开发,并且对于教程是有意义的,因为它使"安装"操作系统独立(数据库即服务也是您可能很好地用于生产数据库的一种方法)。

连接到MongoDB

Mongoose 需要连接到MongoDB数据库。 您可以 require()并使用 mongoose.connect()连接到本地托管数据库,如下所示。

//Import the mongoose module
var mongoose = require('mongoose');

//Set up default mongoose connection
var mongoDB = 'mongodb://127.0.0.1/my_database';
mongoose.connect(mongoDB);

//Get the default connection
var db = mongoose.connection;

//Bind connection to error event (to get notification of connection errors)
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

您可以使用 mongoose.connection 获取默认的 Connection 对象。 连接后,将在 Connection 实例上触发open事件。

提示:如果您需要创建其他连接,可以使用 mongoose.createConnection()。 这采用与 connect()相同的形式的数据库URI(具有主机,数据库,端口,选项等)并返回 Connection 对象)。

定义和创建模型

使用 Schema 界面 定义模型。 模式允许您定义每个文档中存储的字段及其验证要求和默认值。 此外,您可以定义静态和实例帮助程序方法,以便更轻松地使用您的数据类型,以及虚拟属性,您可以像任何其他字段一样使用,但实际上并不存储在数据库中(我们将讨论 下面一点)。

然后使用 mongoose.model()方法将模式"编译"成模型。 一旦你有一个模型,你可以使用它来查找,创建,更新和删除给定类型的对象。

注意:每个模型都会映射到MongoDB数据库中文档集合。 文档将包含在模型 Schema 中定义的字段/模式类型。

Defining schemas

下面的代码片段显示了如何定义一个简单的模式。 首先你 require() mongoose,然后使用Schema构造函数创建一个新的模式实例,在构造函数的对象参数中定义其中的各个字段。

//Require Mongoose
var mongoose = require('mongoose');

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
    a_string          : String,
    a_date            : Date,
});

在上面的例子中,我们只有两个字段,一个字符串和一个日期。 在接下来的部分中,我们将展示一些其他字段类型,验证和其他方法。

Creating a model

使用 mongoose.model()方法从模式创建模型:

// Define schema
var SomeModelSchema = new mongoose.Schema({ name: 'string', });
// Compile model from schema
var SomeModel = mongoose.model('SomeModel', SomeModelSchema );

第一个参数是将为您的模型创建的集合的单一名称(Mongoose将为上述模型 SomeModel 创建数据库集合),第二个参数是您要使用的模式 在创建模型。

注意:定义模型类后,可以使用它们创建,更新或删除记录,并运行查询以获取记录或特定记录子集。 我们将在使用模型部分以及创建视图时向您展示如何执行此操作。

Schema types (fields)

模式可以具有任意数量的字段 - 每个字段表示存储在 MongoDB 中的文档中的字段。 显示许多常见字段类型及其声明方式的示例模式如下所示。

var schema = new Schema(
{
  name: String,
  binary: Buffer,
  living: Boolean,
  updated: { type: Date, default: Date.now },
  age: { type: Number, min: 18, max: 65, required: true },
  mixed: Schema.Types.Mixed,
  _someId: Schema.Types.ObjectId,
  array: [],
  ofString: [String], // You can also have an array of each of the other types too.
  nested: { stuff: { type: String, lowercase: true, trim: true } }
})

SchemaTypes ("type:"后或"字段名称"后的描述符)大部分都是自我说明。 例外情况是:

  • ObjectId: Represents specific instances of a model in the database. For example, a book might use this to represent its author object. This will actually contain the unique ID (_id) for the specified object. We can use the populate() method to pull in the associated information when needed.
  • Mixed: An arbitrary schema type.
  • []: An array of items. You can perform JavaScript array operations on these models (push, pop, unshift, etc.). The examples above show an array of objects without a specified type and an array of String objects, but you can have an array of any type of object.

代码还显示了声明字段的两种方法:

  • Field name and type as a key-value pair (i.e. as done with fields name, binary and living).
  • Field name followed by an object defining the type, and any other options for the field. Options include things like:
    • default values.
    • built-in validators (e.g. max/min values) and custom validation functions.
    • Whether the field is required
    • Whether String fields should automatically be set to lowercase, uppercase, or trimmed (e.g. { type: String, lowercase: true, trim: true })

有关选项的详情,请参阅: SchemaTypes (Mongoose文档)。

Validation

Mongoose提供内置和自定义验证器,以及同步和异步验证器。 它允许您在所有情况下指定可接受的范围或值以及验证失败的错误消息。

内置验证器包括:

  • All SchemaTypes have the built-in required validator. This is used to specify whether the field must be supplied in order to save a document.
  • Numbers have min and max validators.
  • Strings have:
    • enum: specifies the set of allowed values for the field.
    • match: specifies a regular expression that the string must match.
    • maxlength and minlength for the string.

下面的示例(略微修改了Mongoose文档)显示了如何指定一些验证器类型和错误消息:


    var breakfastSchema = new Schema({
      eggs: {
        type: Number,
        min: [6, 'Too few eggs'],
        max: 12
        required: [true, 'Why no bacon?']
      },
      drink: {
        type: String,
        enum: ['Coffee', 'Tea', 'Water',]
      }
    });

有关字段验证的完整信息,请参阅验证(Mongoose文档)。

Virtual properties

虚拟属性是您可以获取和设置但不会持久保存到MongoDB的文档属性。 getter对于格式化或组合字段很有用,而setter对于将单个值解组成多个值以进行存储是有用的。 文档中的示例构造(并解构)来自名字和姓氏字段的全名虚拟属性,这比在模板中使用时构建全名更简单和更清楚。

注意:我们将使用库中的虚拟属性为每个模型记录使用路径和记录的 _id 值定义唯一的网址。

有关详情,请参阅虚拟(Mongoose文档)。

Methods and query helpers

模式还可以具有实例方法 ://mongoosejs.com/docs/guide.html#statics">静态方法 帮助">查询助手。 实例和静态方法是相似的,但有一个明显的区别,即实例方法与特定记录相关联,并且可以访问当前对象。 查询助手允许您扩展mongoose的可链接查询构建器API (例如,允许您添加查询 "byName"除了 find() findOne() findById()方法)。

使用模型

一旦创建了模式,就可以使用它来创建模型。 模型表示数据库中您可以搜索的文档集合,而模型的实例表示可以保存和检索的单个文档。

我们在下面提供一个简要概述。 有关详情,请参阅:模型(Mongoose文档)。

Creating and modifying documents

要创建记录,您可以定义模型的实例,然后调用 save()。 下面的示例假设SomeModel是一个模型(具有单个字段"名称"),我们已从我们的模式创建。

// Create an instance of model SomeModel
var awesome_instance = new SomeModel({ name: 'awesome' });

// Save the new model instance, passing a callback
awesome_instance.save(function (err) {
  if (err) return handleError(err);
  // saved!
});

请注意,创建记录(以及更新,删除和查询)是异步操作 - 您提供在操作完成时调用的回调。 API使用错误第一个参数约定,因此回调的第一个参数将始终为错误值(或null)。 如果API返回一些结果,这将作为第二个参数提供。

您还可以使用 create()在保存模型实例的同时定义模型实例。 回调将返回第一个参数的错误,并为第二个参数的新创建的模型实例返回错误。

SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
  if (err) return handleError(err);
  // saved!
});

每个模型都有一个关联的连接(这将是使用 mongoose.model()时的默认连接)。 您创建一个新的连接并调用 .model()就可以在不同的数据库上创建文档。

您可以使用点语法访问此新记录中的字段,并更改值。 您必须调用 save() update()将修改的值存回数据库。

// Access model field values using dot notation
console.log(awesome_instance.name); //should log 'also_awesome'

// Change record by modifying the fields, then calling save().
awesome_instance.name="New cool name";
awesome_instance.save(function (err) {
   if (err) return handleError(err); // saved!
   });

Searching for records

您可以使用查询方法搜索记录,将查询条件指定为JSON文档。 下面的代码片段显示了如何在网球数据库中找到所有运动员,仅返回运动员姓名 的字段。 这里我们只指定一个匹配字段(运动),但您可以添加更多的条件,指定正则表达式条件,或删除条件,以返回所有运动员。

var Athlete = mongoose.model('Athlete', yourSchema);

// find all athletes who play tennis, selecting the 'name' and 'age' fields
Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
  if (err) return handleError(err);
  // 'athletes' contains the list of athletes that match the criteria.
})

如果指定回调,如上所示,查询将立即执行。 回调将在搜索完成时调用。

注意:Mongoose中的所有回调都使用回调(error,result)模式。 如果在执行查询时发生错误,则 error 参数将包含错误文档, result 将为null。 如果查询成功,则 error 参数将为null,并且 result 将填充查询结果。

如果您未指定回调,API将返回类型为 Query >。 您可以使用此查询对象构建您的查询,然后使用 exec()方法执行(使用回调)。

// find all athletes that play tennis
var query = Athlete.find({ 'sport': 'Tennis' });

// selecting the 'name' and 'age' fields
query.select('name age');

// limit our results to 5 items
query.limit(5);

// sort by age
query.sort({ age: -1 });

// execute the query at a later time
query.exec(function (err, athletes) {
  if (err) return handleError(err);
  // athletes contains an ordered list of 5 athletes who play Tennis
})

上面我们在 find()方法中定义了查询条件。 我们也可以使用 where()函数来做到这一点,我们可以使用点运算符(。)将查询的所有部分链接在一起,而不是单独添加它们。 下面的代码片段与上面的查询相同,有一个额外的年龄条件。

Athlete.
  find().
  where('sport').equals('Tennis').
  where('age').gt(17).lt(50).  //Additional where query
  limit(5).
  sort({ age: -1 }).
  select('name age').
  exec(callback); // where callback is the name of our callback function.

find()方法会获取所有匹配的记录,但通常您只想获取 一个匹配。 以下方法查询单个记录:

请注意:还有 count()方法 您可以使用它来获取符合条件的项目数。 如果要在不实际获取记录的情况下执行计数,这将非常有用。

有很多你可以做的查询。 有关详细信息,请参阅:查询(Mongoose文档)。

您可以使用 ObjectId 模式字段,或者从一个文档到许多使用 ObjectIds 数组的一个文档/模型实例到另一个文档/模型实例创建引用。 该字段存储相关模型的ID。 如果您需要相关文档的实际内容,可以使用填充() >方法在查询中用实际数据替换id。

例如,以下模式定义了作者和故事。 每个作者可以有多个故事,我们表示为 ObjectId 的数组。 每个故事可以有一个作者。 "ref"(下面以粗体突出显示)告诉模式哪个模型可以分配给此字段。

var mongoose = require('mongoose')
  , Schema = mongoose.Schema

var authorSchema = Schema({
  name    : String,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  author : { type: Schema.Types.ObjectId, ref: 'Author' },
  title    : String,
});

var Story  = mongoose.model('Story', storySchema);
var Person = mongoose.model('Author', authorSchema);

我们可以通过分配 _id 值来保存对相关文档的引用。 下面我们创建一个作者,然后一本书,并将作者id分配给我们的故事作者字段。

var bob = new Author({ name: 'Bob Smith' });

bob.save(function (err) {
  if (err) return handleError(err);

  //Bob now exists, so lets create a story
  var story = new Story({
    title: "Bob goes sledding",
    author: bob._id    // assign the _id from the our author Bob. This ID is created by default!
  });

  story.save(function (err) {
    if (err) return handleError(err);
    // Bob now has his story
  });
});

我们的故事文档现在有一个由作者文档ID引用的作者。 为了在我们的故事结果中获得作者信息,我们使用 populate(),如下所示。

Story
.findOne({ title: 'Bob goes sledding' })
.populate('author') //This populates the author id with actual author information!
.exec(function (err, story) {
  if (err) return handleError(err);
  console.log('The author is %s', story.author.name);
  // prints "The author is Bob Smith"
});

注意:精明的读者会注意到我们为我们的故事添加了一位作者,但我们没有做任何事情将我们的故事添加到作者的 stories 数组中。 那么我们如何能得到一个特定作者的所有故事? 一种方法是将我们的作者添加到故事数组中,但这将导致我们有两个地方需要维护有关作者和故事的信息。

更好的方法是获取作者 _id ,然后使用 find()在所有故事的作者字段中搜索。

Story
.find({ author : bob._id })
.exec(function (err, stories) {
  if (err) return handleError(err);
  // returns all stories that have Bob's id as their author.
});

这几乎是您在使用本教学课程相关项目时需要了解的一切 。 有关详细信息,请参阅人口(Mongoose文档)。

每个文件一个模式/模型

虽然可以使用任何您喜欢的文件结构创建模式和模型,但我们强烈建议在其自己的模块(文件)中定义每个模型模式,导出方法以创建模型。 如下所示:

// File: ./models/somemodel.js

//Require Mongoose
var mongoose = require('mongoose');

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
    a_string          : String,
    a_date            : Date,
});

//Export function to create "SomeModel" model class
module.exports = mongoose.model('SomeModel', SomeModelSchema );

然后,您可以在其他文件中立即要求并使用模型。 下面我们展示如何使用它来获取模型的所有实例。

//Create a SomeModel model just by requiring the module
var SomeModel = require('../models/somemodel')

// Use the SomeModel object (model) to find all SomeModel records
SomeModel.find(callback_function);

设置MongoDB数据库

现在我们了解了Mongoose可以做什么,以及我们如何设计我们的模型,现在是开始在 LocalLibrary 网站上工作的时候了。 我们首先要做的是设置一个MongoDb数据库,我们可以使用它来存储我们的库数据。

在本教程中,我们将使用 mLab 的免费云托管的" href ="https://mlab.com/plans/pricing/"> sandbox "数据库。 此数据库层不适合生产网站,因为它没有冗余,但它是伟大的开发和原型。 我们在这里使用它,因为它是免费和容易设置,并且因为mLab是流行的数据库作为服务供应商,你可以合理地选择为您的生产数据库(当时其他流行的选择 的写作包括撰写 .html"> ScaleGrid MongoDB Atlas )。

注意:如果您愿意,可以在本地设置MongoDB数据库,方法是下载并安装 适合您系统的二进制文件。 本文中的其余说明将类似,但连接时要指定的数据库URL除外。

您首先需要使用mLab 创建帐户(这是免费的,只需输入基本的联系方式 并确认其服务条款)。

登录后,您将转到首页屏幕:

  1. Click Create New in the MongoDB Deployments section.
  2. This will open the Create new deployment screen.
    • Select Single-node in the Plan section
    • Select the Sandbox radio button in the Standard Line section
    • Enter a Database Name - for example "local_library"
  3. Click the Create new MongoDB deployment button.

    创建数据库后,您将返回查看您的MongoDb部署的概述。height:266px; width:800px;">

  4. 单击新数据库(如上面突出显示)以打开包含数据库的更多详细信息的视图。 您可以看到数据库没有集合(数据)。
    width:800px;">
    您需要用于访问数据库的URL显示在上面的表单上(针对上面圈出的此数据显示)。 为了使用这个,你需要创建一个数据库用户,你可以在URL中指定。

  5. Click the Users tab and select the Add database user button.
  6. Enter a username and password (twice), and then press Create. Do not select Make read only.

您现在已经创建了数据库,并且具有可用于访问它的URL(具有用户名和密码)。 这将看起来像: mongodb:// your_user_namer:your_password@ds119748.mlab.com:19748 / local_library

安装Mongoose

打开命令提示符,并导航到您创建骨架本地图书馆网站的目录。 输入以下命令以安装Mongoose(及其依赖项)并将其添加到您的 package.json 文件中,除非您在阅读 Mongoose Primer / a>。

npm install mongoose --save

连接到MongoDB

打开 /app.js (在项目的根目录下),并在下面复制以下文本,您在此声明 Express应用程序对象( express(); )。 将数据库url字符串(\' insert_your_database_url_here \')替换为您自己的数据库的位置(即使用来自从mLab 的信息)。

//Set up mongoose connection
var mongoose = require('mongoose');
var mongoDB = 'insert_your_database_url_here';
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

如上所述,在上面的Mongoose引导中,此代码创建与数据库的默认连接,并绑定到错误事件(以便错误将打印到控制台)。

定义LocalLibrary模式

我们将为每个模型定义一个单独的模块,如上所述的 。 首先在项目根目录中为我们的模型创建一个文件夹( / models ),然后为每个模型创建单独的文件:

/express-locallibrary-tutorial  //the project root
  /models
    author.js
    book.js
    bookinstance.js
    genre.js

作者模型

复制下面显示的作者模式代码,并将其粘贴到您的 ./ models / author.js 文件中。 该方案定义了作者对于第一个和家族名称具有 String SchemaTypes,这是必需的并且最多有100个字符,以及 Date 死亡。

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var AuthorSchema = Schema(
  {
    first_name: {type: String, required: true, max: 100},
    family_name: {type: String, required: true, max: 100},
    date_of_birth: {type: Date},
    date_of_death: {type: Date},
  }
);

// Virtual for author's full name
AuthorSchema
.virtual('name')
.get(function () {
  return this.family_name + ', ' + this.first_name;
});

// Virtual for author's URL
AuthorSchema
.virtual('url')
.get(function () {
  return '/catalog/author/' + this._id;
});

//Export model
module.exports = mongoose.model('Author', AuthorSchema);

我们还针对名为"url"的类型声明了一个虚拟,它返回获取模型的特定实例所需的绝对网址 - 我们将使用 每当我们需要获得到特定作者的链接时。

请注意:在模式中将我们的网址声明为虚拟网址是一个好主意,因为这样一来,只需要在一个地方更改项目的网址。
此时,使用此网址的链接 将不工作,因为我们没有任何路由处理代码为个别模型实例。 我们将在后面的文章中设置它们!

在模块的最后,我们导出模型。

书模型

复制下面显示的 Book 模式代码,并将其粘贴到您的 ./ models / book.js 文件中。 其中大部分类似于作者模型 - 我们已经声明了一个带有多个字符串字段的模式和一个用于获取特定书记录的URL的模式,并且我们已经导出了模型。

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookSchema = Schema({
  title: {type: String, required: true},
  author: {type: Schema.ObjectId, ref: 'Author', required: true},
  summary: {type: String, required: true},
  isbn: {type: String, required: true},
  genre: [{type: Schema.ObjectId, ref: 'Genre'}]
});

// Virtual for book's URL
BookSchema
.virtual('url')
.get(function () {
  return '/catalog/book/' + this._id;
});

//Export model
module.exports = mongoose.model('Book', BookSchema);

这里的主要区别是我们已经创建了对其他模型的两个引用(以粗体突出显示):

  • author is a reference to a single Author model object, and is required.
  • genre is a reference to an array of Genre model objects. We haven't declared this object yet!

BookInstance模型

最后,复制下面显示的 BookInstance 模式代码,并将其粘贴到 ./ models / bookinstance.js 文件中。 BookInstance 表示某人可能借用的书的特定副本,并包括有关副本是否可用或其预期的日期,"印记"或版本详细信息的信息。

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookInstanceSchema = Schema({
  book: { type: Schema.ObjectId, ref: 'Book', required: true }, //reference to the associated book
  imprint: {type: String, required: true},
  status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
  due_back: {type: Date, default: Date.now},
});

// Virtual for bookinstance's URL
BookInstanceSchema
.virtual('url')
.get(function () {
  return '/catalog/bookinstance/' + this._id;
});

//Export model
module.exports = mongoose.model('BookInstance', BookInstanceSchema);

我们在这里显示的新东西是字段选项:

  • enum: This allows us to set the allowed values of a string. In this case we use it to specify the availability status of our books (using an enum means that we can prevent mis-spellings and arbitrary values for our status)
  • default: We use default to set the default status for newly created bookinstances to maintenance and the default due_back date to now (note how you can call the Date function when setting the date!)

其他一切都应该从我们以前的模式熟悉。

类型模型 - 挑战!

打开您的 ./ models / genre.js 文件,并创建存储类型(图书类别,例如它是小说还是非小说,浪漫或军事历史等)的架构。

其定义将与其他模型非常相似:

  • The model should have a String SchemaType called name to describe the genre.
  • This name should be required and have between 3 and 100 characters.
  • Declare a virtual for the genre's URL, named url.
  • Export the model.

测试 - 创建一些项目

而已。 我们现在有所有型号的网站设置!

为了测试模型(并创建一些示例书和其他项目,我们可以在下一篇文章中使用),我们现在将运行一个独立脚本来创建每种类型的项目:

  1. Download (or otherwise create) the file populatedb.js in the root of your locallibrary website (in the level above your /models folder).

    注意:您不需要知道 "> populatedb.js 在这一点上工作。 我们将在后面的文章中解释相关的部分。

  2. Enter the following commands in the project root to install the async module that is required by the script (we'll discuss this in later tutorials, )
    npm install async --save
  3. Run the script using node in your command prompt, passing in the URL of your MongoDB database:
    node populatedb <your mongodb url>​​​​
  4. The script should run through to completion, displaying items as it creates them in the terminal.

提示: mLab 上转到您的数据库。 现在,您应该可以深入查看图书,作者,流派和BookInstances的各个集合,并查看单个文档。

概要

在本文中,我们已经了解了一些关于Node / Express上的数据库和ORM,以及有关如何定义Mongoose模式和模型的内容。 然后我们使用这些信息来设计和实现 Book BookInstance Author Genre LocalLibrary 网站。

最后,我们通过创建多个实例(使用独立脚本)来测试我们的模型。 在下一篇文章中,我们将介绍创建一些页面来显示这些对象。

也可以看看

Express Tutorial Part 2: Creating a skeleton website
Express Tutorial Part 4: Routes and controllers
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录
CSS

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }