16

I'm newbie to MongoDB and Backbone, so I try to understand them, but it is hard. I have a big, big problem: I cannot understand how to manipulate attributes in Backbone.Model to use in Views only what I need. More specific - I have a model:

window.User = Backbone.Model.extend({

    urlRoot:"/user",
    idAttribute: "_id",

    defaults: {
        _id: null,
        name: "",
        email: "foo@bar.baz"
    }
});

window.UserCollection = Backbone.Collection.extend({
    model: User,

    url: "user/:id"
});

And I have a View:

beforeSave: function(){
    var self = this;
    var check = this.model.validateAll();
    if (check.isValid === false) {
        utils.displayValidationErrors(check.messages);
        return false;
    }
    this.saveUser();
    return false;
},

saveUser: function(){
    var self = this;
    console.log('before save');
    this.model.save(null, {
        success: function(model){
            self.render();
            app.navigate('user/' + model.id, false);
            utils.showAlert('Success!', 'User saved successfully', 'alert-success');
        },
        error: function(){
            utils.showAlert('Error', 'An error occurred while trying to save this item', 'alert-error');
        }
    });
}

I have to use 'put' method whit data from any fields except '_id', so it must be smth like:

{"name": "Foo", "email": "foo@bar.baz"}

But every time, doesn't depend on what I do it send

{**"_id": "5083e4a7f4c0c4e270000001"**, "name": "Foo", "email": "foo@bar.baz"}

and this error from server:

MongoError: cannot change _id of a document old:{ _id: ObjectId('5083e4a7f4c0c4e270000001'), name: "Foo" } new:{ _id: "5083e4a7f4c0c4e270000001", name: "Bar", email: "foo@bar.baz" }

Github link: https://github.com/pruntoff/habo

Thanks in advance!

Pruntoff
  • 615
  • 3
  • 8
  • 18
  • When you call `model.save()` is a POST being performed or a PUT? – InPursuit Oct 23 '12 at 14:08
  • it is PUT performed. `app.get('/user/:id', usr.findById); app.put('/user/:id', usr.updateUser);` – Pruntoff Oct 23 '12 at 14:38
  • If backbone is doing a PUT when you call `save` then the idAttribute of the model (_id in your case) should not be being sent. Can you call `isNew()` on your model before `save()` and verify that it returns `false`? Also verify that `this.model.id` is not null or undefined before calling `save()` – InPursuit Oct 23 '12 at 21:25

2 Answers2

6

From looking at your mongo error, the problem is not with mongo, it is just doing what it's supposed to do. It had an object with _id of ObjectId type: ObjectId('xxx') and now you're trying to change that object to have an _id of a String type (_id: "5083e4a7f4c0c4e270000001") and that Mongo apparently does not like.

So, the question is: why did the object have an id of type ObjectId in the first place? How did you set it the first time? If you used some other method to initialize it (I'm guessing server side), you should set the id type to be a String so that it is the same as the one coming from your script library. If you want it to stay an ObjectId, you will need to convert the String coming from your script to an ObjectId before you save it to Mongo.

HTH.

TheZuck
  • 3,513
  • 2
  • 29
  • 36
  • Thanks a lot for your answer! I see that problem, but there are a problem in MongoDB too, if I understand right, mongo docs says: *The _id field is immutable. If you try to change its value on an update you will get a "Mod on _id not allowed" error from getLastError.* So I think that I have to dismiss sending _id attr in model, but model need to have it. In all, can you tell me, how to convert string to ObjectId object, when I figur JSON data in model? case request to server has JSON object with _id string, and I cannot manipulate it on server side. – Pruntoff Oct 24 '12 at 06:13
  • I think it's ok to send that field when doing an update but as an identifier to find the object you want to change. I have a feeling that if you send the id in the same format (ObjectId) Mongo will not object (as it is not being changed). I don't know how to convert from String to ObjectId as I don't know what language you're using, but still the question remains: how did it get an ObjectId in the first place? where did the original object come from? – TheZuck Oct 24 '12 at 19:53
  • I take the problem, you were right it is all about formating. Answering your questions: ObjectId gets automaticly in first place by mongoDb when I insert some JSON data. To solve the problem I need no use mongoDB with mongoose instead only mondoDB drivers I use `.Types.ObjectId;`. If I not mistake mongo drivers does not have methods to convert data to ObjectId data. :( Thanks a lot for your help! – Pruntoff Oct 24 '12 at 21:24
6

MongoDB creates _id as an ObjectID, but doesn't retrieve _id as an ObjectID.

Whether this inconsistency is 'correct behaviour' or not, it is certainly an annoying surprise for most MongoDB users.

You can fix it with:

if ( this._id && ( typeof(this._id) === 'string' ) ) {
  log('Fixing id')
  this._id = mongodb.ObjectID.createFromHexString(this._id)
}

See MongoDB can't update document because _id is string, not ObjectId

Community
  • 1
  • 1
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • 1
    Alternatively, you can define `var BSON = mongodb.BSONPure` and use this function: `this._id = new BSON.ObjectID(this._id);` – Collierton Jan 13 '14 at 16:38