如何在宠物交友网站中实现用户与狗狗、活动的关联?
宠物交友网站用户-狗狗-活动关联问题解决方案
问题背景
开发宠物交友网站时,在实现用户与狗狗、活动的关联功能上遇到阻碍,需求是让已登录用户能够查看自己拥有的狗狗以及创建的活动,需要构建有效的关联结构并修正现有实现的问题。
现有代码
User模型
const { Schema, model } = require('mongoose'); const userSchema = new Schema( { firstName: { type: String, unique: true, required: [true, "First Name is required"], trim: true }, lastName: { type: String, unique: true, required: [true, "Last Name is required"], trim: true }, email: { type: String, lowercase: true, required: [true, "Email is required"], unique: true, }, passwordHash: { type: String, required: [true, "HashPassword is required"], trim: true, }, socialMedia: { type: String, enum: ['Instagram', 'Meta', 'X', 'Linkedin'] }, birthday: { type: Date, required: [true, 'Age is required'], }, address: { type: Schema.Types.ObjectId, ref: 'Address' }, civilStatus: { type: String, enum: ['single', 'married', 'just for fun'] }, dogs: [{ type: Schema.Types.ObjectId, ref: 'Dog' }], events: [{ type: Schema.Types.ObjectId, ref: 'Event' }] }, { timestamps: true, } ); const User = model('User', userSchema); module.exports = User;
Dog模型
const { Schema, model } = require("mongoose"); const dogSchema = new Schema( { name: { type: String, required: [true, "Name is required."], trim: true, }, picture: { type: String, required: [true, "Picture is required."], trim: true, }, age: { type: Number, required: [true, "Age is required."], }, breed: { type: String, required: [true, "Breed is required."], }, civilStatus: { type: String, required: [true, "Status is required."], enum: ["single", "married", "looking to breed"], }, size: { type: String, required: [true, "Size is required."], enum: ["big", "medium", "small"], }, owner: { type: Schema.Types.ObjectId, ref: "User", } }, { timestamps: true, } ); const Dog = model("Dog", dogSchema); module.exports = Dog;
用户路由
router.get("/event/:eventId", async (req, res, next) => { const testing = req.params; console.log('id', testing); try { const userList = await User.find().populate('events'); console.log('userList:', userList); res.status(200).json(userList); } catch (error) { next(error); } }); router.get("/dog/:dogId", async (req, res, next) => { console.log(req.params) try { const userList = await User.find().populate("dogs"); res.status(200).json(userList); } catch (error) { next(error); } });
前端展示代码
const [dogs, setDogs] = useState([]); const [users, setUsers] = useState([]); const fetchDogs = async () => { try { const dogResponse = await fetch(`${import.meta.env.VITE_API_URL}/api/dogs`); if (dogResponse.ok) { const dogsData = await dogResponse.json(); setDogs(dogsData) } } catch (error) { console.log(error); } } const fetchUsers = async () => { try { const userResponse = await fetch(`${import.meta.env.VITE_API_URL}/api/users`); if (userResponse.ok) { const usersData = await userResponse.json(); setUsers(usersData) } } catch (error) { console.error("Error fetching data:", error); } } useEffect(() => { fetchDogs() }, []); useEffect(() => { fetchUsers() }, []); let userDogs = combineData(dogs, users) function combineData(dogs, users) { return dogs.map((dog) => { const owner = users.find((user) => user.id === dog.userId); return {...dog, owner}; }); } return ( <> <div> {userDogs.map((dog) => ( <DogCard key={dog.name} {...dog} /> ))} </div> </> );
核心问题分析
- 模型字段不匹配:Dog模型中关联用户的字段是
owner,但前端代码里用dog.userId进行匹配,导致关联失败。 - 双向关联未同步:创建狗狗时没有同步更新对应用户的
dogs数组,导致User和Dog的关联断裂。 - 路由逻辑错误:现有路由查询所有用户并关联资源,不是针对当前登录用户返回专属数据,不符合需求。
- User模型不合理约束:
firstName和lastName设置为unique,重名场景下会导致用户创建失败,应移除该约束。
解决方案实现
1. 修正User模型约束
移除firstName和lastName的unique属性:
firstName: { type: String, required: [true, "First Name is required"], trim: true }, lastName: { type: String, required: [true, "Last Name is required"], trim: true },
2. 优化后端路由(针对登录用户)
添加获取当前用户专属资源的路由,假设登录中间件已将用户信息挂载到req.user:
// 获取当前用户的狗狗列表 router.get("/me/dogs", async (req, res, next) => { try { const currentUser = await User.findById(req.user._id).populate('dogs'); res.status(200).json(currentUser.dogs); } catch (error) { next(error); } }); // 获取当前用户创建的活动列表 router.get("/me/events", async (req, res, next) => { try { const currentUser = await User.findById(req.user._id).populate('events'); res.status(200).json(currentUser.events); } catch (error) { next(error); } });
3. 同步双向关联(创建Dog时)
在Dog的创建路由中,同步更新对应用户的dogs数组:
router.post("/dogs", async (req, res, next) => { try { // 创建狗狗并关联当前用户 const newDog = await Dog.create({ ...req.body, owner: req.user._id }); // 更新用户的狗狗列表 await User.findByIdAndUpdate( req.user._id, { $push: { dogs: newDog._id } }, { new: true } ); res.status(201).json(newDog); } catch (error) { next(error); } });
4. 前端代码优化
直接调用当前用户的专属接口,无需拉取所有用户和狗狗数据:
const [userDogs, setUserDogs] = useState([]); const fetchUserDogs = async () => { try { // 从localStorage获取登录token const token = localStorage.getItem('authToken'); const response = await fetch(`${import.meta.env.VITE_API_URL}/api/users/me/dogs`, { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const dogsData = await response.json(); setUserDogs(dogsData); } } catch (error) { console.error("获取用户狗狗失败:", error); } }; useEffect(() => { fetchUserDogs(); }, []); return ( <div> {userDogs.map((dog) => ( <DogCard key={dog._id} {...dog} /> ))} </div> );
内容的提问来源于stack exchange,提问作者Adrian Bueno
相关产品推荐
相关产品推荐

