在Next.js中从MongoDB填充数据时出现了“Model not registered”的错误。

huangapple go评论93阅读模式
英文:

Model not registered while populating data from mongodb in nextjs

问题

我有3个模型:USER,CATEGORY和PRODUCT。PRODUCT模型有两个字段,引用了其他两个模型。所以当我尝试填充数据时,它会抛出一个错误,说"users"没有注册,对于category也是一样的。

比如,如果我直接访问get_all_product端点而没有登录或收集category(也就是说user和category模型都没有被唤醒),它会抛出category的错误;如果访问category端点,它会显示users没有注册。

我找到的一种修复方法是,如果我从users集合中收集数据,比如用户配置文件,然后从category模型中收集数据,比如收集所有的category,然后访问get_all_product端点,它会按预期给我所有的数据。

但是,如果我尝试在没有访问user或category端点的情况下获取数据,它会抛出一个错误,因为我正在填充user和category。

这不是一个好的方法,因为我正在构建一个电子商务应用程序,访客用户可以浏览类别和产品,所以这给我带来了问题,总是抛出一个users模型未注册的错误,那么这个问题的最佳修复方法是什么?这是我的模型:

Category模型

import mongoose from "mongoose";
import User from "./User";

const CategorySchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, "Please Provide an Name"],
        unique: true,
    },
    slug: {
        type: String,
        required: [true, "Please Provide an Slug"],
        unique: true,
    },
    image: {
        type: String,
        default: '',
    },
    description: {
        type: String,
        required: [true, "Please Provide an Description"],
    },
    status: {
        type: String,
        default: 'inactive',
        enum: ['active', 'inactive']
    },
    isFeatured: {
        type: Boolean,
        default: false,
    },
    addedBy : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
    },
}, {timestamps: true});

const Category =  mongoose.models.categories || mongoose.model('categories', CategorySchema);
export default Category;

Product模型

import mongoose from "mongoose";

const ProductSchema = new mongoose.Schema({
    category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'categories',
    },
    name: {
        type: String,
        required: [true, "Please Provide an Name"], 
    },
    slug: {
        type: String,
        required: [true, "Please Provide an Slug"],
    },
    images: {
        type: [String],
        default: [],
    },
    price: {
        type: Number,
        required: [true, "Please Provide an Price"],
    },
    salePrice: {
        type: Number,
        default: 0,
    },
    quantity: {
        type: Number,
        required: [true, "Please Provide an Quantity"],
    },
    description: {
        type: String,
        required: [true, "Please Provide an Description"],
    },
    status: {
        type: String,
        default: 'inactive',
        enum: ['active', 'inactive', 'rejected']
    },
    isFeatured: {
        type: Boolean,
        default: false,
    },
    addedBy : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
    },
}, {timestamps: true});


const Product =  mongoose.models.products || mongoose.model('products', ProductSchema);
export default Product;

User模型

import mongoose from "mongoose";


const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, "Please Provide an Name"],
        trim: true,
    },
    email: {
        type: String,
        required: [true, "Please Provide an Email"],
        trim: true,
    },
    password: {
        type: String,
        required: [true, "Please Provide an Password"],
        trim: true,
    },
    phoneNumber: {
        type: String,
        trim : true,
    },
    address: {
        type: String,
        trim : true,
    },
    city: {
        type: String,
        trim : true,
    },
    state: {
        type: String,
        trim : true,
    },
    country: {
        type: String,
        trim : true,
    }, 
    postalCode: {
        type: String,
        trim : true,
    },
    isVerified: {
        type: Boolean,
        default: false,
    },
    image : {
        type: String,
        default: '',
    },
    role : {
        type: String,
        default: 'customer',
        enum : ['admin' , 'vendor' , 'customer']
    },
    isVendorApproved : {
        type: String,
        default: 'pending',
        enum : ['pending' , 'approved' , 'rejected']
    },
    isBlackList : {
        type: Boolean,
        default: false,
    },
    pinCode: {
        type: String,
        default: '',
    },
    resetCode: {
        type: String,
        default: '',
    }

},{timestamps : true})

const User = mongoose.models.users || mongoose.model('users' , userSchema);
export default User;

数据库连接

import mongoose, { ConnectOptions } from 'mongoose';


interface connectedOptions extends ConnectOptions{
    useNewUrlParser: boolean,
    useUnifiedTopology: boolean,
}

const options: connectedOptions = {
    useNewUrlParser: true,
    useUnifiedTopology: true,
};

const connectDB = async () => {
    const connectionUrl: string = process.env.DB_URI as string;
    mongoose.connect(connectionUrl , options )
        .then(() => console.log(`Database connected successfully`))
        .catch((err) => console.log("Getting Error from DB connection" + err.message))
    mongoose.set('strictQuery', false);
};

export default connectDB;   

产品端点示例

import { NextRequest, NextResponse } from "next/server";
import connectDB from "@/db";
import Product from "@/models/Product";

export const dynamic = 'force-dynamic'

export async function GET(req: NextRequest) {
    connectDB();
    try {
        const product = await Product.find({}).populate('category').populate('addedBy' )
        return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
    } catch (error: any) {
        console.log("🙀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
        return NextResponse.json({ success: false, msg: "something went wrong !" });
    }
}

注意

在MERN堆栈中,MongoDB始终在应用程序启动时连接,我从来没有遇到过这样的问题,所以可能一个持续的连接可能是一个好的修复方法。

在Next.js中,只有当我们访问某个端点时才连接数据库,正如你可以看到产品路由的示例中调用了connectDB()。

英文:

I have 3 models USER , CATEGORY , PRODUCT . product model has two fields that refer to other two models . so when i try to populate data it throws an error saying

users isn't registered and same for category

Like if i directly hit the get_all_product endpoint without logging in or collecting category ( mean both user and category model not awaked) it throws error for category and if the category endpoint are hitted then it show me users isn't registered

A way to fix that i found is if i collect data from users collection like user profile and then i collected data from category model like collect all category and hit get all product endpoint it give me all data as expected

But if i try to get it without hitting user or category endpoint its throws an error as i am populating user and category

This is not a good approach as i am building a n ecommerce app where an guest user can explore categories and product so this create a problem for me and always throws an error of users model not registered so what the best fix for this issue here is my models

Category model

import mongoose from "mongoose";
import User from "./User";
const CategorySchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please Provide an Name"],
unique: true,
},
slug: {
type: String,
required: [true, "Please Provide an Slug"],
unique: true,
},
image: {
type: String,
default: '',
},
description: {
type: String,
required: [true, "Please Provide an Description"],
},
status: {
type: String,
default: 'inactive',
enum: ['active', 'inactive']
},
isFeatured: {
type: Boolean,
default: false,
},
addedBy : {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
},
}, {timestamps: true});
const Category =  mongoose.models.categories || mongoose.model('categories', CategorySchema);
export default Category;

product model

import mongoose from "mongoose";
const ProductSchema = new mongoose.Schema({
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'categories',
},
name: {
type: String,
required: [true, "Please Provide an Name"], 
},
slug: {
type: String,
required: [true, "Please Provide an Slug"],
},
images: {
type: [String],
default: [],
},
price: {
type: Number,
required: [true, "Please Provide an Price"],
},
salePrice: {
type: Number,
default: 0,
},
quantity: {
type: Number,
required: [true, "Please Provide an Quantity"],
},
description: {
type: String,
required: [true, "Please Provide an Description"],
},
status: {
type: String,
default: 'inactive',
enum: ['active', 'inactive' , 'rejected']
},
isFeatured: {
type: Boolean,
default: false,
},
addedBy : {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
},
}, {timestamps: true});
const Product =  mongoose.models.products || mongoose.model('products', ProductSchema);
export default Product;

usermodel

import mongoose from "mongoose";
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please Provide an Name"],
trim: true,
},
email: {
type: String,
required: [true, "Please Provide an Email"],
trim: true,
},
password: {
type: String,
required: [true, "Please Provide an Password"],
trim: true,
},
phoneNumber: {
type: String,
trim : true,
},
address: {
type: String,
trim : true,
},
city: {
type: String,
trim : true,
},
state: {
type: String,
trim : true,
},
country: {
type: String,
trim : true,
}, 
postalCode: {
type: String,
trim : true,
},
isVerified: {
type: Boolean,
default: false,
},
image : {
type: String,
default: '',
},
role : {
type: String,
default: 'customer',
enum : ['admin' , 'vendor' , 'customer']
},
isVendorApproved : {
type: String,
default: 'pending',
enum : ['pending' , 'approved' , 'rejected']
},
isBlackList : {
type: Boolean,
default: false,
},
pinCode: {
type: String,
default: '',
},
resetCode: {
type: String,
default: '',
}
},{timestamps : true})
const User = mongoose.models.users || mongoose.model('users' , userSchema);
export default User;

db connection

import mongoose, { ConnectOptions } from 'mongoose';
interface connectedOptions extends ConnectOptions{
useNewUrlParser: boolean,
useUnifiedTopology: boolean,
}
const options: connectedOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
const connectDB = async () => {
const connectionUrl: string = process.env.DB_URI as string;
mongoose.connect(connectionUrl , options )
.then(() => console.log(`Database connected successfully`))
.catch((err) => console.log("Getting Error from DB connection" + err.message))
mongoose.set('strictQuery', false);
};
export default connectDB;   

product endpoint example

import { NextRequest, NextResponse } from "next/server";
import connectDB from "@/db";
import Product from "@/models/Product";
export const dynamic = 'force-dynamic'
export async function GET(req: NextRequest) {
connectDB();
try {
const product = await Product.find({}).populate('category').populate('addedBy' )
return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
} catch (error: any) {
console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
return NextResponse.json({ success: false, msg: "something went wrong !" });
}
}

Note

In mern stack where the mongodb is connected always as app starts, I never face such issues so maybe a consistent connection maybe a good fix

In nextjs db connected only when we hit some endpoint as you can see the route example of products calling connectDB()

答案1

得分: 0

注意

> 这只是一个快速修复,而不是永久解决方案。我仍在寻找答案。

我找到的一个快速解决方法是进行一个虚拟查询来注册模型。这是我找到的一个更优的解决方案,而不是从客户端发送3个请求。

import { NextRequest, NextResponse } from "next/server";
import connectDB from "@/db";
import Product from "@/models/Product";
import User from "@/models/User";
import Category from "@/models/Category";

export const dynamic = 'force-dynamic';

export async function GET(req: NextRequest) {
    connectDB();
    try {
        const getAnyUser =  await User.findOne({});
        const getAnyCategory =  await Category.findOne({});
        const product = await Product.find({}).populate('category').populate('addedBy' )
        return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
    } catch (error: any) {
        console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
        return NextResponse.json({ success: false, msg: "something went wrong !" });
    }
}

请注意,这只是一个翻译,我无法提供关于代码的任何解释或建议。

英文:

Note

> this is just a quick fix but not a permanent solution I am still finding the answer

One of Quick fix i findout is doing a dummy query to register the Model.This is optimal solution I figured out rather sending 3 request from client side

import { NextRequest, NextResponse } from "next/server";
import connectDB from "@/db";
import Product from "@/models/Product";
import User from "@/models/User";
import Category from "@/models/Category";
export const dynamic = 'force-dynamic'
export async function GET(req: NextRequest) {
connectDB();
try {
const getAnyUser =  await User.findOne({});
const getAnyCategory =  await Category.findOne({});
const product = await Product.find({}).populate('category').populate('addedBy' )
return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
} catch (error: any) {
console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
return NextResponse.json({ success: false, msg: "something went wrong !" });
}
}

huangapple
  • 本文由 发表于 2023年8月9日 10:39:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864232.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定