7

I have created a Django REST API using Django Rest Framework.

I use 3 different clients to hit this app:

  • Curl (for debugging purposes)
  • Swagger Interface (for debugging purposes)
  • My Angular application

I have a perplexing problem that data returned by the API is being corrupted by Swagger and Angular but not by Curl.

My Django model looks like this:

class MyModel1(CachingMixin, models.Model):
    id = models.BigIntegerField(default=make_id, primary_key=True)
    name = models.CharField(max_length=50, null=False, blank=False,)

The make_id() method referenced above is described here. I recently implemented this change from the standard Django assigned and auto-incremented primary key. As part of that change, I converted id from an IntegerField to a BigIntegerField.

I have some other Django view code (which I have not shown here) that creates an endpoint called GetMyModel1ByName. That endpoint returns a serialized instance of MyModel1. Here is the curl showing what happens when I hit that endpoint. It works perfectly:

$ curl http://localhost:4212/api/getMyModel1ByName/?name=my-name
{"id": 10150133855458395, "name": "my-name"}

Now here is what happens when I hit the same endpoint from Chrome's Developer console:

> $http = angular.element(document.body).injector().get('$http');
> $http.get("http://localhost:4212/api/getMyModel1ByName/?name=my-name").then(function(response) { console.log(JSON.stringify(response.data)) }).catch(function (error) { console.log(error.data) });
{"id":10150133855458396, "name":"my-name"}

As you can see curl reports the ID as 10150133855458395. That's correct. That's what is in the Database. However Angular reports it as 10150133855458396. The final digit is wrong. The difference is 1. It's very surprising!

This is a truly perplexing error. My Django code is so simple that I'm very confident that there is no mistake in it. Instead I feel that change the id field from IntegerField to BigIntegerField might have caused this problem. Why is it happening and what is the solution?? It's ruining the functionality of my application. I'm seeing the same corruption when I hit this endpoint through Swagger.

EDIT: The other questioner is experiencing the same problem I am. It's great to know that I'm not the only one!! However, the answers on that question are not really answers. They don't explain the cause. And they don't tell me how I should solve the issue in my Angular JS code.

9
  • Possible duplicate of JsonResponse from Django - Incorrect values displayed Commented Jun 18, 2016 at 0:27
  • @middlestump Thanks! That question is similar to mine. It's good to know I'm not alone. But there is no actual answer to solve my problem there. Commented Jun 18, 2016 at 2:11
  • What if you simply visit http://localhost:4212/api/getMyModel1ByName/?name=my-name via a browser? Same error? What if you try the angular code again but take out JSON.stringify? Commented Jun 19, 2016 at 22:05
  • @jDo, removing JSON.stringify doesn't make any difference. Commented Jun 19, 2016 at 22:09
  • 1
    @rfkortekaas this has nothing to do with python. It's purely client side. Commented Jun 20, 2016 at 11:10

2 Answers 2

5

You are trying to store a number which is greater than the safe maximum javascript integer value(+/- 9007199254740991).

Your make_id generator should return values with respect to javascript limitations.

For more information read: http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html

Sign up to request clarification or add additional context in comments.

4 Comments

The OP wrote in the comments that "When I simply visit localhost:4212/api/getMyModel1ByName/?name=my-name via a browser (Chrome or Firefox), I get the corrupted ID: {"id":10150133855458396, "name":"my-name"}" Doesn't this mean that the ID is wrong even when JavaScript isn't involved?
curl is the only source you can trust. All browser representations may use javascript.
True true. Curl and wget are more trustworthy than any browser. I just wouldn't expect a browser to parse a response as JSON without actively asking for it (by coding it). Anyway, it might be what's going on here - maybe via an extension/add-on or some built-in browser magic that automatically attempts to cast numerical values and overflows. The ID should have been a string to start with.
Yes! See also similar question: stackoverflow.com/questions/1379934/…
4
+250

TLDR

To cut a long story short. Have your view return the response as String.

{"id": "10150133855458395", "name": "my-name"}

The Long Story

Let's narrow this down. In fact you can and should test this without the involvement of django at all. Please save the json you get from curl http://localhost:4212/api/getMyModel1ByName/?name=my-name into the /static/ folder in your django project. Now when you fetch it django is no longer involved in the picture. You can even host this file on some other kind of server. Let's call it hello.json

Now if you type in http://localhost:4212/static/hello.json you should get.

{"id": 10150133855458395, "name": "my-name"}

Update:

If you don't see the JSON data correctly, that means you have the problem described in the Q&A in the first comment. You have a dodgy extension that messes up the JSON. Disable all extensions and try again. Enable them back one by one.

Now let's create a small angular script.

<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>

  <div ng-app="myApp" ng-controller="myCtrl">

    <h1>{{myjson}}</h1>

  </div>


<script>
   var app = angular.module('myApp', []);
   app.controller('myCtrl', function($scope, $http) {
   $http.get("/static/hello.json")
     .then(function(response) {
        console.log(response.data)
        $scope.myjson = response.data;
      });
   });
</script>

</body>
</html>

Then saving this as angular.html in your static folder, you can load it in your browser as http://localhost:4212/static/angular.html which shows.

{"id":10150133855458396,"name":"my-name"}

This is definitely not the correct result. However there is no BigIntegerField and there is no django here. The issue is entirely with in the browser and angular. We have managed to narrow the problem. it is entirely within angular as you can see from the response that's retrieved by the browser.

Chrome dev tools showing that the correct JSON is retrieved

Angular is choking on large numbers. Send them as strings instead.

3 Comments

It's not so much angular that is choking. It's JavaScript. It does not have separate types for integers. It only has a Number type that it stores as a floating point value.
Thank you. I will implement this ASAP. Assuming it works, I'll mark this works correctly, I'll mark this answer correct.
Its such a shame that the browser/Javascript/Angular doesn't throw an error or warning when dealing with large integers that it can't grok.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.