Pol
Pol

Reputation: 25585

django-tasty-pie turn char field to JSON

I'm using JSONfield to store some JSON formated data in one field. But when I go to my API which build with help of tasty-pie it returns that jason as a string not a nested JSON as I expected.

models.py

class Item(models.Model):
    user = models.ForeignKey(User)
    body = JSONField(max_length=1024)

api.py

 class ItemResource(ModelResource):
    authorization = Authorization()
    authentication = SessionAuthentication()
    list_allowed_methods = ['get', 'post']

    class Meta:
        queryset = Item.objects.filter()

    def get_object_list(self, request):
        return super(ItemResource, self).get_object_list(request).filter(user=request.user)

    def apply_authorization_limits(self, request, object_list):
        return object_list.filter(user=request.user)

    def dehydrate(self, bundle):
        # how to modify bundle['body'] here ? so it work
        return bundle

Obviously JSONfield is sublased from django's standart TextField. So when I retrieve API i receive follow:

 {
    "meta": {
        "limit": 20,
        "next": null,
        "offset": 0,
        "previous": null,
        "total_count": 1
    },
    "objects": [
       {
        "body": "{u'valid': u'json'}",
        "id": 1,
        "resource_uri": "/api/v1/testitem/1/"
        }
    ]
  }

and this is what I want to receive:

 {
    "meta": {
        "limit": 20,
        "next": null,
        "offset": 0,
        "previous": null,
        "total_count": 1
    },
    "objects": [
       {
        "body": {
             "valid': "json"
           },
        "id": 1,
        "resource_uri": "/api/v1/testitem/1/"
        }
    ]
  }

Notice difference in body field?

And if I do:

    def dehydrate(self, bundle):
        bundle['body'] = json.loads(bundle['body'])
        return bundle

I receive this exception:

{"error_message": "Expecting property name: line 1 column 1 (char 1)", "traceback": "Traceback (most recent call last):\n\n  File \"/Users/polinom/Envs/django/lib/python2.7/site-packages/tastypie/resources.py\", line 202, in wrapper\n    response = callback(request, *args, **kwargs)\n\n  File \"/Users/polinom/Envs/django/lib/python2.7/site-packages/tastypie/resources.py\", line 439, in dispatch_list\n    return self.dispatch('list', request, **kwargs)\n\n  File \"/Users/polinom/Envs/django/lib/python2.7/site-packages/tastypie/resources.py\", line 471, in dispatch\n    response = method(request, **kwargs)\n\n  File \"/Users/polinom/Envs/django/lib/python2.7/site-packages/tastypie/resources.py\", line 1270, in get_list\n    bundles.append(self.full_dehydrate(bundle))\n\n  File \"/Users/polinom/Envs/django/lib/python2.7/site-packages/tastypie/resources.py\", line 845, in full_dehydrate\n    bundle = self.dehydrate(bundle)\n\n  File \"/Users/polinom/workspace/microjob/applications/examinations/api.py\", line 24, in dehydrate\n    bundle.data['body'] = json.loads(bundle.data['body'])\n\n  File \"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py\", line 326, in loads\n    return _default_decoder.decode(s)\n\n  File \"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py\", line 360, in decode\n    obj, end = self.raw_decode(s, idx=_w(s, 0).end())\n\n  File \"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py\", line 376, in raw_decode\n    obj, end = self.scan_once(s, idx)\n\nValueError: Expecting property name: line 1 column 1 (char 1)\n"}

Upvotes: 1

Views: 1118

Answers (1)

borges
borges

Reputation: 3687

After creating the bundle(s), the response is created by serializing the content. For json format and default Serializer, it first convert any complex type to python basic types and then dumps it in a string representation.

As you can see, in the case of a original string, it justs convert it to unicode. You need to convert it to a dict before serialize the resource. So, in the dehydrate method you can do:

def dehydrate(self, bundle):
    bundle['body'] = json.loads(bundle.data['body'])
    return bundle

Since you are only modifying one element, you can also can do:

def dehydrate_body(self, bundle):
    return json.loads(bundle.data['body'])

The JSONField stores a python dict representation, not a JSON representation. So, my first attempt is incorrect. You can return eval(bundle.data['body']). IMO the use of eval here is safe because the JSONField ensures that the content is a statement that can be directly translated to a json representation.

Upvotes: 4

Related Questions