0

In my application, I have two models - Book and Genre. I referenced the Genre model from the Book model using Schema.Types.ObjectId. So this is how my models look like:

Book model

const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const Schema = mongoose.Schema

const bookSchema = Schema({
  name: {
    type: String,
    trim: true,
    required: 'Please enter a book name'
  },
  description: {
    type: String,
    trim: true
  },
  author: {
    type: String,
    trim: true,
  },
  category: {
    type: String,
    trim: true
  },
  genre: [{
    type: Schema.Types.ObjectId,
    ref: 'Genre'
  }]
})

module.exports = mongoose.model('Book', bookSchema)

Genre model

const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const Schema = mongoose.Schema

const genreSchema = Schema({
  name: {
    type: String,
    trim: true,
    required: 'Please enter a Genre name'
  }
})

module.exports = mongoose.model('Genre', genreSchema)

On the book edit page, I want to be able to show the genres available and check the ones already saved in that particular book.

Here is what I have for my routes:

router.get('/edit/:id', (req, res, next) => {
  const book = Book.findOne({ _id: req.params.id })
  .populate({
    path: 'genre',
    model: 'Genre',
    populate: {
      path: 'genre',
      model: 'Book'
    }
  })
  .exec()
    .then((book) => {
      const genres = Genre.find({ 'genre': req.params.id })
      res.render('editBook', { book, genres })
    })
    .catch((err) => {
      throw err
    })
})

router.post('/edit/:id', (req, res, next) => {
  req.checkBody('name', 'Name is required').notEmpty()
  req.checkBody('description', 'Description is required').notEmpty()
  req.checkBody('category', 'Category is required').notEmpty

  const errors = req.validationErrors()

  if (errors) {
    console.log(errors)
    res.render('editBook', { book, errors })
  }

  const book = Book.findOneAndUpdate({ _id: req.params.id }, req.body,
  {
    new: true,
    runValidators:true
  }).exec()
  .then((book) => {
    res.redirect(`/books/edit/${book._id}`)
  })
  .catch((err) => {
    res.send({
      'message': err
    })
  })
})

And the part where the genre should be displayed looks like this:

.form-group
          label.col-lg-2.control-label Genre
          .col-lg-10
            for genre in genres
              .checkbox
                input.checkbox(type='checkbox', name='genre', id=genre._id, value=genre._id, checked=genre.checked)
                label(for=genre._id) #{genre.name}

What am I possibly doing wrong? I have tried all the solutions I know but nothing has worked.

P.S: mongoose-deep-populate plugin has not been updated for a long time. The solution I used worked fine for the show routes and can be found here - https://stackoverflow.com/a/43464418/2119604

kinsomicrote
  • 187
  • 4
  • 20

1 Answers1

0

You're using mongose populate method incorrectly. populate returns the object which a parent and referenced model (s). In your case book has an array of genres (btw, it has wrong name in your schema, single instead of plural), so you don't need additional request to load genres:

router.get('/edit/:id', (req, res, next) => {
  Book
    .findOne({ _id: req.params.id })
    .populate('genre')
    .exec()
    .then(book => res.render('editBook', { book, genres: book.genre }))
    .catch(err => {
      console.log(err);
      next(err);
    });
});
alexmac
  • 19,087
  • 7
  • 58
  • 69