无法编译模型,因为已经被编译过了 Mongoose

205

我不确定我做错了什么,这是我的check.js文件:

var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));

var a1= db.once('open',function(){
var user = mongoose.model('users',{ 
       name:String,
       email:String,
       password:String,
       phone:Number,
      _enabled:Boolean
     });

user.find({},{},function (err, users) {
    mongoose.connection.close();
    console.log("Username supplied"+username);
    //doSomethingHere })
    });

这是我的insert.js文件

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/event-db')

var user = mongoose.model('users',{
     name:String,
     email:String,
     password: String,
     phone:Number,
     _enabled:Boolean
   });

var new_user = new user({
     name:req.body.name,
     email: req.body.email,
     password: req.body.password,
     phone: req.body.phone,
     _enabled:false
   });

new_user.save(function(err){
    if(err) console.log(err); 
   });

每当我尝试运行check.js时,都会出现以下错误:

无法在编译后覆盖“users”模型

我知道这个错误是由Schema不匹配造成的,但我看不出具体哪里出了问题?我对mongoose和nodeJS相当陌生。

以下是我从MongoDB客户端界面上获取的信息:

MongoDB shell version: 2.4.6 connecting to: test 
> use event-db 
  switched to db event-db 
> db.users.find() 
  { "_id" : ObjectId("52457d8718f83293205aaa95"), 
    "name" : "MyName", 
    "email" : "myemail@me.com", 
    "password" : "myPassword", 
    "phone" : 900001123, 
    "_enable" : true 
  } 
>

这是我从MongoDB客户端接口获取的信息: MongoDB shell版本:2.4.6 连接到:测试
use event-db 切换到数据库event-db db.users.find() { "_id" : ObjectId("52457d8718f83293205aaa95"), "name" : "MyName", "email" : "myemail@me.com", "password" : "myPassword", "phone" : 900001123, "_enable" : true }
- Anathema.Imbued
有时候我们犯的错误很愚蠢,就像我的情况一样:导出文件的代码是这样的{userModel:model("user",userSchema)...所以每次访问该文件时都会重新创建模型并触发错误...因此,不要像这样导出,而是创建一个常量"const userModel=model("user",userSchema),然后像这样导出module.exports = { userModel }。 - Bakaji
50个回答

303

您可能会遇到此错误的另一个原因是,如果您在不同的文件中使用相同的模型,但require路径的大小写不同。

例如,在我的情况下,我在一个文件中有require('./models/User'),然后在另一个需要访问User模型的文件中,我有require('./models/user')

我猜查找模块和mongoose时将其视为不同的文件。确保两者的大小写匹配后,问题就不再存在了。


14
这确实是一个非常棘手的问题 - 我认为它与操作系统有关(因为只会在 Mac 和 Windows 上发生,文件系统会忽略大小写)。我也遇到了这个问题,但幸运的是看到了你的回答 :) 非常感谢 Jonnie! - Miroslav Nedyalkov
9
这个问题发生在我的OS X系统中。 - lutaoact
这对我来说也是一样的。万岁OS X及其(默认情况下不区分大小写的)文件系统。 - mithril_knight
3
谢谢你的帮助。这正是我在MacOS上遇到的问题。 - Sebastian Korotkiewicz
我也遇到同样的问题。谢谢。 - Mark Watson

168

错误发生的原因是您已经定义了一个模式,然后又重新定义了该模式。通常您应该实例化模式一次,然后在需要时由全局对象调用它。

例如:

user_model.js

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

var userSchema = new Schema({
   name:String,
   email:String,
   password:String,
   phone:Number,
   _enabled:Boolean
});
module.exports = mongoose.model('users', userSchema);          

check.js

var mongoose = require('mongoose');
var User = require('./user_model.js');

var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
  User.find({},{},function (err, users) {
    mongoose.connection.close();
    console.log("Username supplied"+username);
    //doSomethingHere 
  })
});

插入.js

var mongoose = require('mongoose');
var User = require('./user_model.js');

mongoose.connect('mongodb://localhost/event-db');
var new_user = new User({
    name:req.body.name
  , email: req.body.email
  , password: req.body.password
  , phone: req.body.phone
  , _enabled:false 
});
new_user.save(function(err){
  if(err) console.log(err); 
});

99
避免导出/需要模型 - 如果任何一个模型具有对其他模型的 'ref',这可能会导致依赖问题。使用 var User = mongoose.model('user') 代替 require - wprl
1
在定义测试模式迁移代码后更改模式实际上可能是有用的。 - Igor Soarez
3
@wprl,您能否进一步解释一下?为什么需要它会造成问题? - varuog
3
这个答案是误导性的。事实上,如果只有一个MongoDB服务器实例和多个数据库,如果在另一个应用程序中定义一个已被占用的数据库,那么就会出现这样的错误。就是这么简单。 - Carmine Tambascia
从理论上讲,这没有太多意义,因为 import 只会被调用一次。再次调用它不会执行给定的创建脚本,而只应返回已分配的导出内容。 - Jakub Keller

133

在“watching”测试时,我遇到了这个问题。当测试被编辑时,监视器会重新运行测试,但由于这个原因它们失败了。

我通过检查模型是否存在,如果存在则使用它,否则创建它来解决这个问题。

import mongoose from 'mongoose';
import user from './schemas/user';

export const User = mongoose.models.User || mongoose.model('User', user);

3
很遗憾,mongoose.models 在最近版本中已经不存在了。 - Pedro Luz
3
我曾遇到同样的问题,但通过在所有测试之前清除所有模型来解决:for (let model in mongoose.models) delete mongoose.models[model] - E. Sundin
4
在使用最新版本的Mongoose中,我发现这个功能非常好用! - Dana Woodman
1
这对我有用。 - Cuado
3
这个答案帮助我解决了一个问题。当我修改代码后,热模块替换重新加载页面时出现了错误信息“error - OverwriteModelError: Cannot overwrite Brand model once compiled.” - Yersskit
显示剩余5条评论

67

在进行单元测试时,我遇到了这个问题。

第一次调用模型创建函数时,mongoose会将该模型存储在您提供的键下(例如“users”)。如果您使用相同的键多次调用模型创建函数,则mongoose不会允许您覆盖现有的模型。

您可以通过以下方式检查mongoose中是否已存在该模型:

let users = mongoose.model('users')

如果模型不存在,这将会抛出一个错误,因此你可以将其包裹在try/catch中,以获取模型或创建它。
let users
try {
  users = mongoose.model('users')
} catch (error) {
  users = mongoose.model('users', <UsersSchema...>)
}

1
+1 我也遇到了同样的问题,需要在定义模式之前设置一些插件配置。这与mocha完全不兼容,最终我放弃了,并采用了try catch方法。 - Victor Parmar
我使用相同的方式,但是反过来,这很糟糕: exports.getModel = ()-> mongoose.model('User', userSchema) 捕捉错误 exports.getModel = ()-> mongoose.model('User')``` - Andi Giga
1
非常感谢您,先生。我在这个问题上浪费了5个多小时。我正在使用无服务器架构,而不是我习惯的Node服务器。 - mxdi9i7
2
2021年更新:export default mongoose.models["user"] ?? mongoose.model("user", schema) - kshksdrt

53

如果你正在使用Serverless离线模式,而不想使用--skipCacheInvalidation选项,那么你可以直接使用以下命令:

module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);

即使使用了 --skipCacheInvalidation,如果您正在将一个模型导入到另一个模型中,仍然必须使用此选项。 - Powderham
11
这正是我在寻找的准确答案,可以用于Next.js。我希望它能排在页面更靠前的位置! - Brendan Nee
1
但问题在于,Typescript无法检测到在接口中定义的User模型,它会回退到any类型。 - rony
3
在我尝试在 Next.js 中使用 mongoose 时,我从未遇到过这个问题,直到它发生了。 这个解决方案对我有用,谢谢! 我认为这是因为 Next.js 的开发模式配置的原因。也许 Next.js 团队可以改进一下这个问题... - Shaman
@rony 我曾经在使用TypeScript时遇到过这个问题,并通过以下方式解决:const getModel = () => model("User", UserSchema); module.exports = (models.User || getModel()) as ReturnType<typeof getModel>; - bozdoz
这在 Next JS (TypeScript) 中对我很有效。 - josealvarez97

26

我一直遇到这个问题,但不是因为模式定义,而是因为无服务器离线模式 - 我刚刚通过以下方式解决了它:

serverless offline --skipCacheInvalidation

这里提到的是https://github.com/dherault/serverless-offline/issues/258

希望这能对其他在使用Serverless并且运行离线模式的项目有所帮助。


6
我发现跳过缓存失效和不断重新加载很烦人,相反这个方法很有效:module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema); - Will Bowman
这对我非常有用,直到我在另一个模型中再次导入模型。为了防止这个错误,我们需要使用@asked_io提供的解决方案。 - Powderham
尝试了@Moosecunture的解决方案,但是出现了错误。但是它起作用了。谢谢。 - Sugam Malviya

20
如果你来到这里,可能你有和我一样的问题。我的问题是我定义了另一个同名的模型。我把我的画廊和文件模型都叫做"File"。该死的复制粘贴!

17
我通过添加解决了这个问题。
mongoose.models = {}

在这行之前:

mongoose.model(<MODEL_NAME>, <MODEL_SCHEMA>)

希望它能解决你的问题。

这就是我所做的,它解决了问题。mongoose.connection.models = {}; - Fortune
Typescript 抛出错误 无法分配给 models,因为它是只读属性 - bhavesh
1
这难道不会清除你之前定义的所有模型吗?! - Ali80
是的...不要这样做。它会清除Mongoose到目前为止缓存的所有模型,并且会不断地重新创建和缓存每个请求您的服务器收到的模型。这很糟糕,无论是在性能方面还是在故障排除方面。请改用其中之一 这些方法 之一 - Jack_Hu

14

点击这里!官方示例。 最重要的事情是像这样导出

export default mongoose.models.Item || mongoose.model('Item', itemsSchema)

这似乎只是导出一个普通模型,我失去了所有附加到我的用户模型的自定义方法,例如 属性'findOrCreateDiscordUser'在类型'Model <any,{},{}>'上不存在。 - dcsan

13

当我像这样写的时候,发生了这件事:

import User from '../myuser/User.js';

然而,正确的路径是'../myUser/User.js'


混合使用模式路径的大小写,在导入时似乎会引起这个问题 - 请检查导入模式的所有文件是否使用相同的大小写。 - Andrew Cupper

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