15

I am having some issue here. I am trying to return a JSON response made of a message and a model instance:

   class MachineModel(models.Model):
       name = models.CharField(max_length=64, blank=False)
       description = models.CharField(max_length=64, blank=False)
       manufacturer = models.ForeignKey(Manufacturer)
       added_by = models.ForeignKey(User, related_name='%(app_label)s_%(class)s_added_by')
       creation_date = models.DateTimeField(auto_now_add=True)
       last_modified = models.DateTimeField(auto_now=True)

    machine_model_model = form.save(commit=False)
    r_user = request.user.userprofile
    machine_model_model.manufacturer_id = manuf_id
    machine_model_model.added_by_id = request.user.id
    machine_model_model.save()
    alert_message = " The'%s' model " % machine_model_model.name
    alert_message += ("for '%s' " % machine_model_model.manufacturer)
    alert_message += "was was successfully created!"
    test = simplejson.dumps(list(machine_model_model))
    data = [{'message': alert_message, 'model': test}]
    response = JSONResponse(data, {}, 'application/json')


class JSONResponse(HttpResponse):
"""JSON response class."""
    def __init__(self, obj='', json_opts={}, mimetype="application/json", *args, **kwargs):
        content = simplejson.dumps(obj, **json_opts)
        super(JSONResponse,self).__init__(content, mimetype, *args, **kwargs)

But I keep getting:

File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")

TypeError: <MachineModel: "Test12"> is not JSON serializable

Why is that? I have seen before:

models = Model.objects.filter(manufacturer_id=m_id)
json = simplejson.dumps(models)

and that works... what is the difference?!

Thanks!

Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
abisson
  • 4,365
  • 9
  • 46
  • 68

2 Answers2

19

You should use django serializers instead of simplejson:

For example, this returns correctly serialized data:

from django.core import serializers
# serialize queryset
serialized_queryset = serializers.serialize('json', some_queryset)
# serialize object
serialized_object = serializers.serialize('json', [some_object,])
Serhii Holinei
  • 5,758
  • 2
  • 32
  • 46
  • 1
    I don't really get a JSON object... I get "[{"pk": 86, "model": "machine_models.machinemodel", "fields": {"name": "MX10", "description": "lol", "creation_date": "2012-09-23T16:50:35.709Z", "last_modified": "2012-09-23T16:50:35.709Z", "added_by": 2, "manufacturer": "1"}}]" – abisson Sep 23 '12 at 16:51
  • 1
    It is actualy JSON object. You can deserialize it on a template. By jquery, for example: `jQuery.parseJSON(response)`, which returns a javascript dictionary. – Serhii Holinei Sep 23 '12 at 17:25
  • Ok good it works! But I always get an array (in JQuery and I have to take the first [0]... Is that normal?! It is kind of annoying as well. Would you recommend a REST Api?! – abisson Sep 23 '12 at 17:28
  • As _the second argument of serialize method can be any iterator that yields Django model instances_ (taken from docs), I dont know any other method to handle with single model instance serialization. Also, checkout this [relative question](http://stackoverflow.com/questions/757022/how-do-you-serialize-a-model-instance-in-django). About REST Api - sorry, I am not competent in this area. Btw, I use the same `description`s in my projects :-) – Serhii Holinei Sep 23 '12 at 17:38
17

Method-1: Use Django's python serializer

I think this answer won't return a JSON or a Python dict/list object. So, use the format python instead of json

from django.core import serializers
# serialize queryset
serialized_queryset = serializers.serialize('python', some_queryset)
# serialize object
serialized_object = serializers.serialize('python', [some_object,])

Django shell response

In [2]: from django.core import serializers                                                                                                                             

In [3]: qs = SomeModel.objects.all()                                                                                                                                    

In [4]: json_res = serializers.serialize('json',qs)                                                                                                                     

In [5]: type(json_res)                                                                                                                                                  
Out[5]: str

In [6]: python_res = serializers.serialize('python',qs)                                                                                                                 

In [7]: type(python_res)                                                                                                                                                
Out[7]: list
#views.py
from django.core import serializers
from django.http.response import JsonResponse


def some_view(request):
    some_queryset = SomeModel.objects.all()
    serialized_queryset = serializers.serialize('python', some_queryset)
    return JsonResponse(serialized_queryset, safe=False)

Method-2: Use Django's values() method

The direct use of values() method will throw TypeError exception, so convert the QuerySet to a python list as below,

from django.http.response import JsonResponse


def sample_view(request):
    return JsonResponse(list(SomeModel.objects.all().values()), safe=False)
Community
  • 1
  • 1
JPG
  • 82,442
  • 19
  • 127
  • 206
  • @Radesh What happened with method-1? – JPG Aug 06 '19 at 07:19
  • 1
    return json response contain some extra /" like this : [{\"model\": \"api\" – Radesh Aug 06 '19 at 07:55
  • @JPG, is it equally the same to use either of these methods? Do any of these have any drawbacks in performance or another thing? I personally see using second method very easy but also curious about if it has any disadvantage. – Ulvi Jul 29 '20 at 22:23
  • I'm not a big fan of using `safe=false`, but in my application, I was able to put the list into a dictionary to make the `JsonResponse` serializer happy: `return JsonResponse({ "someObjects": list(SomeModel.objects.all().values()) })` – Joe Sadoski Sep 23 '21 at 13:15