Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving Backbone.js model data. Data not sent correctly

I'm new to Backbone.js and I'm trying to save a model instance. I'm using django as my server.

Client side Code:

var Song = Backbone.Model.extend({
    defaults: {
        name: 'New Song'
    },
    url: function() {
        return window.location.href;
    }

});

var song = new Song()
song.save()

csrfmiddlewaretoken is set properly before the data is sent.

I stepped through the jQuery $.ajax function called internally by Backbone.sync and found that the model object contains the correct data.

However, the request.POST received by the server is

POST:<QueryDict: {u'[object Object]': [u'']}>

instead of the actual data. Any idea where I'm going wrong?

Update: I did a quick fix by setting Backbone.emulateJSON to true. But according to the comments in the Backbone (0.9.2) code it is meant for legacy servers. I'm using Django 1.4.1. Does it mean django 1.4.1 is incompatible?

Update 2: When I set Backbone.emulateJSON to false, I get the following error in firefox but it fails silently in chrome.

   "[Exception... "Component returned failure code: 0x80460001 
(NS_ERROR_CANNOT_CONVERT_DATA)"  nsresult: "0x80460001 (NS_ERROR_CANNOT_CONVERT_DATA)"

  location: "JS frame :: http://localhost:8000/static/jquery.js :: <TOP_LEVEL> :: line 8214"  data: no]"

I'm using jQuery for ajax as preferred by Backbone and it seems the error might be in jQuery.

Update 3: I solved it by overriding the $.ajax used by Backbone.sync with my own. It is still a quick fix.

Backbone.js verson: 0.9.2

jQuery version: 1.8.0. Also tried with 1.7.2. Same result.

like image 348
Pramod Avatar asked Aug 30 '12 19:08

Pramod


2 Answers

I was having a similar issue and through some detective work/luck I figured it out. The issue is that by default Backbone sends the POST data as a JSON encoded string in the body of the request not as a part of the request.POST QueryDict. So to get the data in this case you would have to use the python json library and call json.loads(request.body) in the Django view to read the data properly.

As an aside, the reason that setting Backbone.emulateJSON = true; works is because then Backbone sends the JSON to Django through the "legacy" mechanism which makes it appear in the request.POST QueryDict.

like image 116
Etan Avatar answered Nov 08 '22 12:11

Etan


If you want the data to be available in the QueryDict request.POST, then you will have to override the Backbone.sync method.

First of all you will have to set Backbone.emulateJSON to true.

You can have a look at the Backbone.sync method over here. You will notice that the model attributes are stringified.

if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
  params.contentType = 'application/json';
  params.data = JSON.stringify(options.attrs || model.toJSON(options));
}

Edit this part of the function to:

if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
  params.contentType = 'application/json';
  if(options.emulateJSON){
     params.data = options.attrs || model.toJSON(options);
  }else{
     params.data = JSON.stringify(options.attrs || model.toJSON(options));
  }
}

On some other line you will notice that Backbone adds a 'model' key to the POST QueryDict.

params.data = params.data ? {model: params.data} : {};

Edit this line to:

params.data = params.data ? params.data : {};

Thats it! Now you will have the data as a part of request.POST QueryDict.

like image 25
GunnerFan Avatar answered Nov 08 '22 14:11

GunnerFan