Могу ли я отправить массив объектов в представление, в контроллере, связанном с объектом mongodb в node / express / mongoose? - PullRequest
0 голосов
/ 05 марта 2019

Я следовал руководству на сайте MDN для Node / express / mongoose.Это может быть знакомо многим, но я все равно опишу код.Я хочу создать представление, похожее на страницу book_list, однако мне бы хотелось иметь возможность отправлять экземпляры книги с каждой книгой (подробности будут приведены ниже).Другими словами, я хотел бы иметь возможность иметь BookInstances для каждой книги как часть объекта книги на странице списка - это в основном для подсчета (или длины), но я могу также использовать его другими способами.

Модель книги

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookSchema = new Schema({
title: {type: String, required: true},
author: { type: Schema.ObjectId, ref: 'Author', required: true },
summary: {type: String, required: true},
isbn: {type: String, required: true},
genre: [{ type: Schema.ObjectId, ref: 'Genre' }]
});

// Virtual for this book instance URL.
BookSchema
.virtual('url')
.get(function () {
  return '/catalog/book/'+this._id;
});

// Export model.
module.exports = mongoose.model('Book', BookSchema);

Часть схемы BookInstance модели:

var BookInstanceSchema = new Schema(
{
  book: { type: Schema.Types.ObjectId, ref: 'Book', required: true },//reference to the associated book
  imprint: { type: String, required: true },
  status: { type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance' },
  due_back: { type: Date, default: Date.now }
 }
);

Контроллер списка книг:

// Display list of all Books.
exports.book_list = function(req, res, next) {

Book.find({}, 'title author')
  .populate('author')
  .exec(function (err, list_books) {
    if (err) { return next(err); }
    //Successful, so render
    res.render('book_list', { title: 'Book List', book_list: list_books });
  });

};

Контроллер детализации книги:

// Display detail page for a specific book.
exports.book_detail = function(req, res, next) {

async.parallel({
    book: function(callback) {

        Book.findById(req.params.id)
          .populate('author')
          .populate('genre')
          .exec(callback);
    },
    book_instance: function(callback) {

      BookInstance.find({ 'book': req.params.id })
      .exec(callback);
    },
}, function(err, results) {
    if (err) { return next(err); }
    if (results.book==null) { // No results.
        var err = new Error('Book not found');
        err.status = 404;
        return next(err);
    }
    // Successful, so render.
    res.render('book_detail', { title: 'Book Detail', book: results.book, 
book_instances: results.book_instance } );
  });

   };

У меня такое чувство, что это может быть что-то, что можно сделать с помощью заполнить , но у меня не получается, чтобы это работало.Единственный способ, которым мне удалось заставить объект экземпляра книги отображаться в списке для каждого элемента книги, - это отправить все экземпляры книги в представление.Оттуда я использую цикл foreach , а затем оператор IF , чтобы получить экземпляры книги для каждой книги.Это выглядит ужасно, и я уверен, что должен быть какой-то другой способ сделать это.Я привык к asp.net mvc - в этом вы используете виртуальный объект.Я не уверен, если я должен изменить модель здесь или контроллер.Возможно, я также захочу передать гораздо более сложную модель со списками в списках.

Я заметил, что жанр фактически сохраняется в документе книги в отличие от bookinstances - отсюда и строки в контроллере деталей книги:

  book_instance: function(callback) {
  BookInstance.find({ 'book': req.params.id })
  .exec(callback);
},

Ниже я показал, что я сделал.Я мог бы также сделать это как объекты в контроллере, но теперь у меня есть следующее:

Контроллер книг:

exports.book_list = function (req, res, next) {

async.parallel({
    books: function (callback) {
        Book.find()
            .exec(callback)
    },
    bookinstances: function (callback) {
        BookInstance.find()
            .exec(callback)
    },
}, function (err, results) {
    if (err) { return next(err); } // Error in API usage.
    // Successful, so render.
    res.render('book_list', { title: 'Book Detail', books: results.books, 
bookinstances: results.bookinstances });
});
};

Код book_list.pug:

extends layout

block content
 h1= title

table.table
th Book
th BookInstance Count
th 
//- above th is for buttons only (no title)
each book in books
  - var instCount = 0
  each bookinstance in bookinstances
    if book._id.toString() === bookinstance.book.toString()
      - instCount++
  tr 
    td 
      a(href=book.url) #{book.title}
    td #{instCount}

    td
      a.btn.btn-sm.btn-primary(href=book.url+'/update') Update
      if !instCount
        a.btn.btn-sm.btn-danger(href=book.url+'/delete') Delete

else
  li There are no books.

Как выглядит страница:

enter image description here

1 Ответ

0 голосов
/ 09 марта 2019

Была обнаружена проблема, когда я пытался использовать MongoDb как реляционную базу данных, а не как тип документа. Решением этой проблемы является использование массива BookInstances в документе Book таким же образом, как жанр:

var BookSchema = new Schema({
title: {type: String, required: true},
author: { type: Schema.ObjectId, ref: 'Author', required: true },
summary: {type: String, required: true},
isbn: {type: String, required: true},
genre: [{ type: Schema.ObjectId, ref: 'Genre' }],
bookInstances: [{ type: Schema.ObjectId, ref: 'BookInstance' }]
});

Все детали можно сохранить в документе BookInstance, поскольку _id - это все, что требуется в документе Book. Каждый раз, когда BookInstance добавляется, его можно вставить в массив Book / BookInstances (этот пост помогает: Вставка элементов в массив mongo через mongoose ). Это также означает, что BookInstance необходимо удалить (вытащить) из массива, а также из документа, содержащего его данные.

Теперь mongoose populate () можно использовать обычным способом.

...