You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose报错:User.findById is not a function,关联模型同步删除问题

解决Mongoose循环依赖导致的User.findById is not a function错误

你遇到的这个问题核心是模型循环依赖:你的User模型导入了Leave,同时Leave模型又导入了User。Node.js模块加载机制下,这种互相引用会导致其中一个模型在加载时还没完成导出,所以你拿到的User对象是未完全初始化的,自然没有findById这类方法。

另外,你的pre('remove')中间件还有异步操作未正确等待的问题,这会导致数据同步不彻底,我一起帮你修复:

步骤1:修复循环依赖问题

在Leave模型中,不要通过require('../models/user')导入User,而是直接使用Mongoose已经注册的模型:mongoose.model('User')。这样就绕开了模块循环引用的问题。

步骤2:修复异步操作的等待逻辑

pre('remove')中间件里的数据库操作都是异步的,直接调用next()会在操作完成前就执行后续流程,必须等待异步操作完成后再调用next(),可以用async/await让代码更清晰。

修改后的Leave模型代码

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// 移除循环引用的require,改用mongoose.model获取已注册的模型
const { ObjectID } = require('mongodb');

var schema = new Schema({
  userId: { type: Schema.Types.ObjectId, ref: 'User' },
  status: { type: String, required: true },
  start: { type: String, required: true },
  end: { type: String, required: true },
  type: { type: String, required: true }
});

// 改用async/await处理异步操作
schema.pre('remove', async function(next) {
  try {
    const leave = this;
    // 获取User模型
    const User = mongoose.model('User');
    const user = await User.findById(new ObjectID(leave.userId));
    if (user) {
      user.filedLeaves.pull(leave._id);
      await user.save();
    }
    next();
  } catch (err) {
    next(err); // 传递错误给错误处理中间件
  }
});

module.exports = mongoose.model('Leave', schema);

修改后的User模型代码

同样修复循环依赖和异步等待问题:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const muv = require('mongoose-unique-validator');
// 移除循环引用的require,改用mongoose.model获取已注册的Leave模型
const { ObjectID } = require('mongodb');

var schema = new Schema({
  fullName: { type: String, required: true, unique: true },
  leaveCredits: { type: Number },
  filedLeaves: [{ type: Schema.Types.ObjectId, ref: 'Leave' }]
}, { usePushEach: true });

schema.plugin(muv);

// 改用async/await处理批量删除Leave的异步操作
schema.pre('remove', async function(next) {
  try {
    const user = this;
    const Leave = mongoose.model('Leave');
    // 用Promise.all等待所有删除操作完成
    await Promise.all(user.filedLeaves.map(item => 
      Leave.findByIdAndRemove(new ObjectID(item))
    ));
    next();
  } catch (err) {
    next(err);
  }
});

module.exports = mongoose.model('User', schema);

额外说明

  • 为什么mongoose.model('User')能生效?因为当你调用mongoose.model('User', schema)导出模型时,Mongoose会把这个模型注册到全局的模型缓存里,后续通过mongoose.model('User')就能直接获取到完整的模型对象,不管模块加载顺序如何。
  • 使用async/await和Promise.all能确保所有异步操作完成后再执行后续逻辑,避免数据不同步的问题,同时也让错误处理更统一。

内容的提问来源于stack exchange,提问作者Nar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:58:22