Mongoose的.pre('init')何时被调用?

4
我希望创建一个拥有独特访问“代码”的“游戏”。该代码在模式中是必需的,因此每次创建新游戏时都需要生成一个代码。 我认为schema.pre('init')是生成该访问代码的好地方。
GameSchema.pre('init', function(next) {
    // Code generation logic happens here
    this.code = myNewlyGeneratedCode
    next()
}

很遗憾,这会返回一个错误消息:ValidationError: Game validation failed: code: Path 'code' is required.

为什么它不起作用?我是否必须在实例化新游戏之前创建一个code


当您检索文档时,将调用“init”事件。 您需要使用.pre('save')代替。 - JJJ
我一直在避免使用.pre('save'),因为我不想每次保存游戏文档更改时重新生成游戏代码。如我下面的评论所提到的,我认为.pre('validate')是我要找的。 - Delta_HF
“验证”也被称为每次对文档进行更改时都会调用,因此您无法通过它避免该问题。 - JJJ
啊,是的,说得好。我想我需要像lineus建议的那样在pre-save中使用this.isNew。感觉Mongoose应该对这种情况有更好的支持;似乎这种情况非常普遍。 - Delta_HF
经过进一步测试,我发现我不能使用 pre-save,因为 MongoDB 仍然抱怨需要 code 路径。最终我使用 pre-validate 中的 this.isNew 完成了工作。 - Delta_HF
在自动填充数据的模式中,不应该使用“required”属性。实际上,最清晰的解决方案是向创建代码的函数中添加一个“default”值。 - JJJ
1个回答

11

如评论所述,pre('save')是在将文档存储到数据库之前运行的中间件。pre('init')会在从MongoDB查询返回文档时调用。

展示文档中间件顺序的最简单方法是使用一个简单的例子:

49768723.js

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const Schema = mongoose.Schema;

var count = 0;

const schema = new Schema({
  name: String
});

function log(str) {
  console.log(`${++count}: ${str}`);
}

schema.pre('save', function () {
  log('pre-save');
});

schema.pre('init', function () {
  log('pre-init');
});

schema.post('save', function () {
  log('post-save');
});

schema.post('init', function () {
  log('post-init');
});

schema.pre('validate', function () {
  log('pre-validate');
});

schema.post('validate', function () {
  log('post-validate');
});

schema.pre('remove', function () {
  log('pre-remove');
});

schema.post('remove', function () {
  log('post-remove');
});


const Test = mongoose.model('test', schema);

const test = new Test({ name: 'Billy' });

async function main() {
  await test.save();
  log('saved');
  await Test.findOne({ _id: test.id });
  log('found');
  await test.remove();
  log('removed');
  return mongoose.connection.close();
}

main();

输出

stack: ./49768723.js
1: pre-validate
2: post-validate
3: pre-save
4: post-save
5: saved
6: pre-init
7: post-init
8: found
9: pre-remove
10: post-remove
11: removed
stack:

非常好的例子,谢谢!看起来pre-validate是我应该使用的,因为我不想在每次保存游戏文档更改时重新生成代码。 - Delta_HF
2
你可以在预保存钩子中使用类似于“if (this.isNew) { this.code = gencode() }”的测试来生成代码。除非您稍后明确地再次设置它,否则this.isNew仅在第一次保存时为true。我认为这种策略略微更清晰,因为您的钩子的意图是“保存”特殊属性而不是验证某些内容。 - lineus
我想问一下,这个执行顺序背后的逻辑是什么?我被术语搞糊涂了,通常认为“init”是大多数流程中发生的第一件事情,但在这里,首先执行了保存前和保存后的完成,然后才执行预初始化和后初始化。 - gaurav1999

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接