#javascript #node.js #mongodb #mongoose
Вопрос:
Я пытаюсь показать «события», созданные «художником», на странице художника, но сталкиваюсь с ошибкой «художник.события не повторяются». Ниже приведена моя модель «художник» (модели-> artists.js):
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportlocalMongoose = require('passport-local-mongoose');
const artistSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
location: {
type: String,
required: [true, 'Hometown (so you can be paired with local venues)']
},
genre: {
type: String
},
joined_date: {
type: Date,
default: Date.now
},
about: String,
size: Number,
});
artistSchema.plugin(passportlocalMongoose);
module.exports = mongoose.model('Artist', artistSchema);
Далее идет моя модель событий (которая связывает художников с событием через массив «художник».
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const eventSchema = new Schema({
event_name: String,
location: String,
description: String,
image: String,
artist: {
type: Schema.Types.ObjectId,
ref: 'Artist'
},
});
module.exports = mongoose.model('Event', eventSchema);
Вот где я сталкиваюсь со своей реальной проблемой — когда я пытаюсь перечислить события в разделе «Исполнители», я сталкиваюсь с ошибкой. Ниже приведена страница показа художников (просмотры-> художники->> показать.ejs)
<% layout('layouts/boilerplate') %>
<div class="row">
<div class="col-12">
<div class="card mb-3">
<img src="<%= artist.image %>" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"><%= artist.username %></h5>
<p class="card-text">Genre: <%= artist.genre %></p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">Location: <%= artist.location %></li>
<li class="list-group-item">Number of people in group: <%= artist.size %></li>
<li class="list-group-item">About: <%= artist.about %></li>
</ul>
<% if (currentUser amp;amp; artist.equals(currentUser._id)) {%>
<div class="card-body">
<a class="card-link btn btn-primary" href="/artists/<%=artist.id%>/edit">Edit</a>
<form class="d-inline" action="/artists/<%=artist.id%>?_method=DELETE" method="POST">
<button class="btn btn-primary">Delete</button>
<% } %>
</form>
<% for(let events of artist.events) { %>
<div class="class mb-3">
<p>Event name: <%= event.event_name %></p>
</div>
<% } %>
</div>
</div>
<div class="card-footer text-muted">
Back to
<a href="/artists">All Artists</a>
</div>
</div>
</div>
Полная ошибка:
TypeError: /Users/chaseschlachter/mtapp/views/artists/show.ejs:22
20| <% } %>
21| </form>
>> 22| <% for(let events of artist.events) { %>
23| <div class="class mb-3">
24| <p>Event name: <%= event.event_name %></p>
25| </div>
artist.events is not iterable
Добавление маршрутов моих исполнителей для контекста (маршруты-> artists.js):
const express = require('express');
const router = express.Router();
const passport = require('passport');
const Artist = require('../models/artist');
const catchAsync = require('../utils/catchAsync');
const ExpressError = require('../utils/ExpressError');
/* lists artists from database */
router.get('/', async (req, res) => {
const artists = await Artist.find({});
res.render('artists/index', { artists })
});
router.get('/new', (req, res) => {
res.render('artists/new');
});
/* shows specific artists that exist in database */
router.get('/:id', catchAsync(async(req, res,) => {
const artist = await Artist.findById(req.params.id);
if (!artist) {
req.flash('error', 'Cannot find that Artist');
return res.redirect('/artists');
}
res.render('artists/show', { artist });
}));
/* artist edits form*/
router.get('/:id/edit', catchAsync(async (req, res) => {
const artist = await Artist.findById(req.params.id);
if (!artist) {
req.flash('error', 'Cannot find that Artist');
return res.redirect('/artists');
}
res.render('artists/edit', { artist });
}))
router.put('/:id', catchAsync(async (req, res) => {
const { id } = req.params;
const artist = await Artist.findByIdAndUpdate(id, { ...req.body.artist });
res.redirect(`/artists/${artist._id}`);
}))
Что я делаю не так?
Ответ №1:
Глядя на вашу схему художников, вы не определили поле событий, поэтому нет ничего, что нужно повторять. Этого поля просто нет внутри объекта художника, оно не определено.
Ваша система artistSchema создает коллекцию объектов художника. Эти объекты содержат только поля, указанные вами в определении схемы. В то время как у вас есть другая коллекция объектов событий, которые полностью отделены от исполнителей, определенных в схеме событий.
Поскольку вы хотели бы связать художников с событиями, у вас есть несколько вариантов для этого:
- Поддерживайте список событий в виде массива внутри схемы исполнителя (потенциально состоящего из объектов, которые ссылаются на объекты события).
- Попросите события сохранить ссылку на исполнителя (как вы делаете в настоящее время), а затем запросить события, используя _id исполнителя.
- Не храните события в виде отдельной коллекции, а вместо этого встраивайте объекты событий в виде массива в схему исполнителя.
Каждая стратегия имеет свои плюсы и минусы (подробнее об этом читайте здесь: https://docs.mongodb.com/manual/applications/data-models-relationships/)
Я бы подумал, что вам лучше всего было бы выбрать вариант 2, так как в варианте 1 вам также нужно будет убедиться, что любые новые события или удаления событий отражены в модели художника.
На самом деле, поскольку вы используете мангуста, вы можете реализовать то, что хотите, с помощью виртуалов (https://mongoosejs.com/docs/populate.html#populate-virtuals) следующим образом:
artistSchema.virtual('events', {
ref: 'Event', // The model to use
localField: '_id', // Find events where `localField`
foreignField: 'artist', // is equal to `foreignField`
justOne: false // we can have more than 1 event per artist
});
Теперь нам нужно только заполнить этот массив событий, для этого есть много способов, одним из которых было бы:
artist.events // == undefined
artist.populate('events').execPopulate();
artist.events // == [event1, event2, etc...]
Если вы добавляете этот виртуальный объект в свой объект исполнителя и заполняете свой объект исполнителя до вашего фрагмента кода, он должен выполняться должным образом. Как я уже упоминал, это всего лишь один из способов достичь этого.
Комментарии:
1. Привет @дэвид Горски — спасибо за исчерпывающий ответ. Я думаю, что знаю, как выполнить компонент «заполнить» (благодаря вашей помощи), но qq в схеме виртуального исполнителя.. будет ли artistSchema.virtual существовать в новой модели или я могу добавить в нижнюю часть своей модели художника в виде массива?
2. Еще раз спасибо за помощь. Прочитайте документы, которые я добавил к виртуальному художнику в нижней части моей модели художника (как ее собственный массив), но изо всех сил пытаюсь добавить строку заполнения в мои маршруты художников (добавлено выше) без «Ошибки типа: Не удается прочитать свойство»заполнять » неопределенного». Куда должна идти эта линия?
3. Случайно отметил, что на этот вопрос дан ответ, но на него нет ответа.
4. Привет @econobro, на первый вопрос я обычно просто добавляю виртуальный вызов после объявления моей схемы (например, как у вас есть вызов плагина после объявления схемы). Что касается второй проблемы, с которой вы сталкиваетесь: похоже, что ваш документ исполнителя не определен, поэтому кажется, что проблема связана с кодом, приведшим к тому, что вы опубликовали. Убедитесь, что вы правильно запросили и дождались нужного исполнителя, прежде чем пытаться заполнить события. Извините, трудно сказать, что не так без полной картины.
5. Нет проблем — спасибо за дополнительный контекст @David Gorski!