使用Mongoose .populate()无法获取数据及路由跳转异常问题
Mongoose Populate 关联数据渲染问题(Listing与Blog一对多)
问题现象
Listing与Blog为一对多关系,通过Listing的blogs字段关联Blog,原本selectedBlog功能可正常展示单篇博客详情。但修改EJS中的路由链接后,出现以下问题:
- 博客列表仅显示“...”,无法正常展示
blogCreator信息和描述内容 - 点击链接后无法打开博客详情,且无法切换回列表状态
修改点:将EJS中的链接从
<a href="/listings/<%= listing._id %>/blogs/<%= blog._id %>"
改为
<a href="/listings/<%= listing._id %>/blogs"
相关代码
Listing Schema
const listingSchema = new Schema({ title : { type : String, required : true, }, description : String, image : { type : String, default : "https://images.app.goo.gl/oMwfut7SSe1GwpyS7", set : (v) => v === undefined ? "https://images.app.goo.gl/oMwfut7SSe1GwpyS7" : v, }, price : Number, location : String, country : String, reviews : [ { type : Schema.Types.ObjectId, ref : "Review", }, ], blogs : [ { type : Schema.Types.ObjectId, ref : "Blog", }, ], owner : { type : Schema.Types.ObjectId, ref : "User", }, }); const Listing = mongoose.model("Listing", listingSchema); module.exports = Listing;
Blog Schema
const blogSchema = { blogCreator : { type: Schema.Types.ObjectId, ref : "User", }, description :{ type : String, required : true, }, amenities : { type : String, required : true, }, rating : { type : Number, min : 1, max : 5 }, convenience : { type : String, required : true, }, checkIn : { type : String, required : true, }, security : { type : String, required : true, }, cancellation : { type : String, required : true, }, pets : { type : String, required : true, }, }; let Blog = mongoose.model("Blog", blogSchema); module.exports = Blog;
EJS模板代码
<div class="list-group"> <% listing.blogs.forEach(blog => { %> <a href="/listings/<%= listing._id %>/blogs/<%= blog._id %>" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"> <div> <strong><%= blog.blogCreator %></strong> <p class="mb-1"><%= blog.description ? blog.description.substring(0, 50) : '' %>...</p> </div> </a> <% }) %> </div>
路由代码
router.get('/', wrapAsync(async (req, res) => { const listing = await Listing.findById(req.params.id) .populate({ path: "blogs", populate: { path: "blogCreator", model: 'User' } }) .populate({ path : "reviews", populate : { path: "author", model: 'User' } }) .populate("owner","User"); if (!listing) { return res.status(404).send('Listing not found'); } res.render('listings/show', { listing }); })); router.get('/:blogId', wrapAsync(async (req, res) => { const listing = await Listing.findById(req.params.id) .populate({ path: 'blogs', populate: { path: 'blogCreator', model: 'User' } }) .populate({ path : "reviews", populate : { path: "author", model: 'User' } }) .populate("owner","User"); const blog = await Blog.findById(req.params.blogId).populate('blogCreator'); if(!listing){ req.flash("error","Listing you requested for does not exist!"); res.redirect("/listings"); } if (!blog) { req.flash("error", "Blog you requested for does not exist!"); return res.redirect(`/listings/${listing._id}`); } res.render('listings/show', { listing, selectedBlog: blog }); }));
问题分析与解决方案
1. 核心Schema定义错误
Blog Schema未使用new Schema()初始化,而是直接用普通对象,导致Mongoose无法正确识别字段结构,populate后无法获取description、blogCreator等字段值,最终模板仅显示“...”。
修复代码:
// 修改Blog Schema定义 const blogSchema = new Schema({ blogCreator : { type: Schema.Types.ObjectId, ref : "User", }, description :{ type : String, required : true, }, // 其他字段保持不变 });
2. 路由链接逻辑错误
修改后的链接指向博客列表路由(/listings/:id/blogs),无法触发单篇博客的详情路由(/listings/:id/blogs/:blogId),导致selectedBlog参数永远无法传入模板,自然无法展示详情,也不存在“切换回列表”的逻辑触发点。
修复方案:
- 若需点击列表项进入详情,恢复原链接:
<a href="/listings/<%= listing._id %>/blogs/<%= blog._id %>" - 若需跳转至列表页面,需在模板中添加明确的返回按钮逻辑,区分列表与详情状态:
<% if (selectedBlog) { %> <!-- 单篇博客详情展示 --> <div class="blog-detail"> <h2><%= selectedBlog.blogCreator.username %></h2> <p><%= selectedBlog.description %></p> <!-- 其他详情字段 --> <a href="/listings/<%= listing._id %>/blogs" class="btn btn-secondary">返回博客列表</a> </div> <% } else { %> <!-- 博客列表展示 --> <div class="list-group"> <% listing.blogs.forEach(blog => { %> <a href="/listings/<%= listing._id %>/blogs/<%= blog._id %>" class="list-group-item list-group-item-action"> <strong><%= blog.blogCreator.username %></strong> <p><%= blog.description.substring(0,50) %>...</p> </a> <% }) %> </div> <% } %>
3. 模板渲染细节优化
直接输出blog.blogCreator会显示对象字符串([object Object]),需改为输出用户具体字段(如username),确保展示内容符合预期。
内容的提问来源于stack exchange,提问作者Priya
相关产品推荐
相关产品推荐

