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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 14:14:52