So I have the following layout for my app:
UserSchema (contains username and password)
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
username: String,
password: String
}, options);
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
StudentSchema (discriminator() on User) with additional fields
var StudentSchema = new mongoose.Schema({
indexNumber: String,
courses: [{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Course"
},
entryDate: {
type: Date,
default: Date.now
}
}]
}, options);
module.exports = User.discriminator("Student", StudentSchema);
TeacherSchema (discriminator() on User) with additional fields
var TeacherSchema = new mongoose.Schema({
degree: String,
courses: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Course"
}]
}, options);
module.exports = User.discriminator("Teacher", TeacherSchema);
I am using passport local-strategy to User.register().
router.post("/register", function(req,res) {
var newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
username: req.body.username
});
User.register(newUser, req.body.password, function(err, user) {
if(err) throw err;
passport.authenticate("local")(req, res, function() {
req.flash("success", "Welcome " + user.username + ".");
res.redirect("/");
});
});
});
Now all this is under same collection users in db (because of discriminators)
How do I, after an user registers, "promote" him to student or teacher, so I can have those additional fields in his object?
Try setting __t to "Teacher" or "Student" using an update command on the User model. I think the default discriminator key is __t so you need something like:
user.update({$set: {__t: "Student" } }, callback)`
Then you'll need to pull it with the Discriminator again to get your fields:
const Student = User.discriminator("Student", StudentSchema);
Student.findById(id, callback)
Related
Edit #2: Working code
This does what I want it to do now:
// route
app.post("/users/:id/createStudent", function(req, res){
Student.create(function(err){
if(err){
console.log(err)
res.redirect("/")
} else {
const newStudent = new Student({
firstName: req.body.firstName,
lastName: req.body.lastName,
age: req.body.age,
instrument: req.body.instrument,
});
newStudent.save()
.then(() => Parent.findById(req.params.id))
.then((parent) => {
parent.students.push(newStudent);
return parent.save();
});
req.flash("success", "Successfully Created Student");
res.redirect("/users/:id");
}
})
});
// parent model
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
passportLocalMongoose = require("passport-local-mongoose");
const ParentSchema= new Schema({
username: String,
lastName: String,
email: String,
password: String,
students: [],
});
ParentSchema.plugin(passportLocalMongoose);
const Parent = mongoose.model("parent", ParentSchema);
module.exports = Parent;
Brand new developer here. I'm trying to build an app where a parent user can create a student user and a teacher user can assign said student weekly tasks. I'm having trouble with the create student route. I'm not sure how to get the student object to store the parent's id properly. I've been trying to find a solution for a few days now and can't seem to find anything that deals with this specifically and the docs haven't helped either. I hope I'm just missing something and one of you kind folks can at least point me in the right direction.
Here's my code:
//parent model
const mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
const parentSchema= new mongoose.Schema({
username: String,
lastName: String,
email: String,
password: String,
student: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Student"
},
username: String,
}
});
parentSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Parent", parentSchema);
// student model
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema({
firstName: String,
lastName: String,
age: String,
instrument: String,
parent: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Parent"
},
username: String
},
});
module.exports = mongoose.model("Student", studentSchema);
// create student route
app.post("/users/:id/createStudent", function(req, res){
const newStudent = new Student({
firstName: req.body.firstName,
lastName: req.body.lastName,
age: req.body.age,
instrument: req.body.instrument,
parent: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Parent"
},
username: String
},
});
Parent.findById(req.params.id, function(err, foundParent){
if(err){
req.flash("error", "Something went wrong");
console.log(err);
res.redirect("/users/:id");
} else {
const parent = foundParent
Student.create(newStudent, parent, function(err, student){
if(err){
console.log(err);
} else {
student.parent.id = req.parent._id;
student.parent.username = req.parent.username;
student.save();
parent.student.push(newStudent);
parent.save();
req.flash("success", "Successfully Created Comment");
res.redirect("/users/" + parent._id);
}
})
}
});
});
In the mongo shell, db.students.find() puts out this as my student object after I fill out the form:
db.students.find()
{ "_id" : ObjectId("5f0b33facd6fa70355f14774"), "firstName" : "Johnny", "lastName" : "Apple", "age" : "2014-03-04", "instrument" : "drums", "parent" : { "username" : "function String() { [native code] }" }, "__v" : 0 }
Sorry for the long code. I've just been trying so many things that it's added up to what you see here. Thanks for any advice you might give and for reading this far.
Edit
Updated code:
app.post("/users/:id/createStudent", function(req, res){
Parent.findById(req.params.id, function(err, foundParent){
if(err){
req.flash("error", "Something went wrong");
console.log(err);
res.redirect("/users/:id");
} else {
const newStudent = new Student({
firstName: req.body.firstName,
lastName: req.body.lastName,
age: req.body.age,
instrument: req.body.instrument,
parent: {
id: foundParent,
username: foundParent.username
},
});
Student.create(newStudent, foundParent, function(err, student){
if(err){
console.log(err);
} else {
student.parent._id = foundParent._id;
student.parent.username = foundParent.username;
student.save();
foundParent.student.push(newStudent);
foundParent.save();
req.flash("success", "Successfully Created Student");
res.redirect("/users/" + foundParent._id);
}
})
}
});
});
So the new error this is producing is DocumentNotFoundError: No document found for query "{ _id: 5f0a7026e330500413ee599a }" on model "Student" which I assume is because I don't have populate in there. I'm just not sure where the populate option would fit in?
mongoose.Schema.Types.ObjectId is a type not a value.
Try using mongoose.Schema.Types.ObjectId(req.parent.id)
Edit
Another option to try:
Move the const newStudent = block inside the else block of the findById callback.
Inside the new Student( constructor, use
parent: {
id: foundParent,
username: foundParent.username
}
Because of the schema definition, when you save the document, mongoose should store a DBRef in the student collection, which can then be reconstituted with populate.
Working with Mongoose "Populate" - So far I'm unable to successfully get the "Food" model to populate the "User" model.
The goal is to be able to save a "Food" to a user.
USER MODEL:
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId}],
easy: {type: Boolean, default: false},
});
UserSchema.plugin(passportLocalMongoose)
module.exports = mongoose.model("User", UserSchema);
FOOD MODEL:
var foodSchema = new mongoose.Schema({
name: { type: String, required: false, unique: true },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
}
});
module.exports = mongoose.model("Food", foodSchema);
GET ROUTE
router.get("/dashboard", function (req, res) {
User.find({currentUser: req.user})
.populate({path: 'foods'}).
exec(function (err, foods) {
if (err) return (err);
console.log('The food is:', req.user.foods.name);
});
});
POST ROUTE:
router.post("/dashboard", function(req, res, next) {
User.update({ id: req.session.passport.user }, {
}, function(err, user) {
if (err) return next(err);
User.findById(req.user._id, function(err, user) {
var newFood = new Food({
name: req.body.currentBreakfast,
image: 'test',
});
user.foods = newFood
user.save();
});
});
res.redirect('/dashboard');
});
You need to add the ref field in your user schema for foods to be populated while querying user.
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Food' }],
easy: {type: Boolean, default: false},
});
You can user this query.
await User.find({currentUser: req.user}).populate('foods')
Try this it will auto-populate data
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId,ref: 'Food'}}],
easy: {type: Boolean, default: false},
});
UserSchema.pre('find', prepopulate)
function prepopulate(){
return this.populate('foods')
}
Below is the code for User model, Post model and the route. I need to query the DB and then pass to the view through the router. What am I missing?
User model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
module.exports = mongoose.model('User', userSchema);
Posts model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var postSchema = new Schema({
postTitle: {
type: String,
required: true
},
postStory: {
type: String
},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model('Post', postSchema);
And here is the query in the router that I'm trying but apparently it's not the way to go...
My GET route:
router.get('/dashboard', isLoggedIn, function(req, res) {
Post.find({author:req.user._id}, (err, posts) => {
if(err) {
console.log(err);
} else {
res.render('users/dashboard', {currentUser: req.user, posts: posts});
}
});
});
What am I missing here?
You may want to change the find query to match the proper attribute in the following line:
Post.find({author: req.user._id}, (err, posts) => {
to be:
Post.find({'author.id': req.user._id}, (err, posts) => {
Read more: http://mongoosejs.com/docs/2.7.x/docs/finding-documents.html
I have been trying to populate from a user table and have been unsuccessful. Any help would be appreciated.
I am checking a variable isProvider
if(true)
then the data is saved in a provider table
else
in a customer table.
I want the user table to be an Auth table, so I want to populate a field called "userId" in these models. The id is being saved. When i print the results of populate, It shows a populated json but when i see it in the database it shows only the Id. I want to access the details of user table through the photographer table. How do i achieve this ?
User model
/*
* Title: User model
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
//Data model
var UserSchema = new Schema({
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
token: String,
mobile: String,
type: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
isPhotographer: {type: Boolean, default: false},
verified: {type: Boolean, default: false}
});
UserSchema.pre('save', function(next) {
var user = this;
if(this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if(err) {
return next(err);
}
bcrypt.hash(user.password, salt, null, function (err, hash) {
if ( err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.methods.comparePassword = function (passw, cb) {
bcrypt.compare(passw, this.password, function( err, isMatch) {
if(err) {
return cb(err);
}
cb(null, isMatch);
});
};
UserSchema.plugin(passportLocalMongoose);
user = mongoose.model('User', UserSchema);
module.exports = user;
Provider model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var providerSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
ref: 'User'
},
firstName: String,
lastName: String,
profilePicture: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GFS'
},
email: String,
phone: Number,
address: String,
dob: Date,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
providerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
provider= mongoose.model('provider', providerSchema);
module.exports = provider;
Customer model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var customerSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
firstName: String,
lastName: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
customerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
customer = mongoose.model('Customer', customerSchema);
module.exports = customer;
Controller
if(user.isProvider) {
var provider= new providermodel({
userId: user._id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
provider.save(function(err, docs) {
if(!err) {
pprovidermodel.findOne({_id: provider._id}).populate('userId').exec(function(err, docs) {
if(err) {
console.log(err);
}
else {
console.log(docs); ----> **Here populate works, but no changes in the database**
console.log("SO " + docs.userId.email);
}
})
}
})
}else {
var customer = new customermodel({
userId: user.id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
customer.save(function(err) {
if(!err) {
customermodel.findOne({}).populate('userId').exec(function(err, docs)
{
console.log(err);
console.log(docs);
})
}
})
}
I think it's right. Populate don't change values in database only retrieve values when code is running.
I am new to mongoose and nodejs. Please help me to understand what is wrong in my code while saving collection. My user.js looks like below
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema ({
name: { type: String, required: true },
username:{ type:String, required: true, index:{ unique: true}},
password:{ type: String, required: true, select: false},
email:{ type: String, required: true, select: true},
mobile:{ type: String, required: true, select: true},
nativecid:{ type: Schema.Types.ObjectId, ref:'City'},
regdate:{ type: Date, default: Date.now },
did:{ type: String, required: false }
});
UserSchema.pre('save', function(next){
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash){
if(err) return next(err);
user.password = hash;
next();
});
});
UserSchema.methods.comparePassword = function(password){
var user = this;
return bcrypt.compareSync(password, user.password);
};
module.exports = mongoose.model('User', UserSchema);
And my City model ie city.js looks like below
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CitySchema = new Schema({
name: { type: String, required: true },
status: { type: Boolean, default: true },
date: { type: Date, default: Date.now }
});
module.exports = mongoose.model( 'City', CitySchema );
City is already stored in database as below
{ "_id" : ObjectId("56a4a0adb0f445561cfd4e37"), "name" : "New York", "date" : ISODate("2016-01-24T10:00:13.220Z"), "status" : 1, "__v" : 0 }
I want this ObjectId of New York should be saved in user collection while user signups. I am trying like below but not succeed.
var User = require('../models/user');
var City = require('../models/city');
api.post('/signup', function(req, res){
var user = new User({
name: req.body.name,
username: req.body.username,
password: req.body.password,
email: req.body.email,
mobile: req.body.mobile
});
City
.findOne({ name: "New York" })
.populate('_id')
.exec(function (err, city1) {
if(err){
res.send(err);
return;
}
console.log('The creator is %s', city1.name);
return user.nativecid = city1._id;
})
user.save(function(err){
if(err){
res.send(err);
return;
}
res.json({
success: true,
message: 'User created successfully!'
});
});
});
I am not understanding what is wrong. Please advice me.
Just save user in city.findOne (nested) and may be no need to populate you should use projection.
City.findOne({ name: "New York" }, {name:1}) // ID return defaulat
.exec(function (err, city1) {
if(err){
res.send(err);
return;
}
console.log('The creator is %s', city1.name);
user.nativecid = city1._id;
user.save(function(err1){
if(err1){
res.send(err1);
return;
}
res.json({
success: true,
message: 'User created successfully!'
});
});
})