Django Rest Framework and JSONField

PythonDjangoDjango ModelsDjango Rest-Framework

Python Problem Overview


Given a Django model with a JSONField, what is the correct way of serializing and deserializing it using Django Rest Framework?

I've already tried crating a custom serializers.WritableField and overriding to_native and from_native:

from json_field.fields import JSONEncoder, JSONDecoder
from rest_framework import serializers

class JSONFieldSerializer(serializers.WritableField):
    def to_native(self, obj):
    return json.dumps(obj, cls = JSONEncoder)
	
    def from_native(self, data):
        return json.loads(data, cls = JSONDecoder)

But when I try to updating the model using partial=True, all the floats in the JSONField objects become strings.

Python Solutions


Solution 1 - Python

If you're using Django Rest Framework >= 3.3, then the JSONField serializer is now included. This is now the correct way.

If you're using Django Rest Framework < 3.0, then see gzerone's answer.

If you're using DRF 3.0 - 3.2 AND you can't upgrade AND you don't need to serialize binary data, then follow these instructions.

First declare a field class:

from rest_framework import serializers

class JSONSerializerField(serializers.Field):
    """ Serializer for JSONField -- required to make field writable"""
    def to_internal_value(self, data):
        return data
    def to_representation(self, value):
        return value

And then add in the field into the model like

class MySerializer(serializers.ModelSerializer):
    json_data = JSONSerializerField()

And, if you do need to serialize binary data, you can always the copy official release code

Solution 2 - Python

In 2.4.x:

from rest_framework import serializers # get from https://gist.github.com/rouge8/5445149

class WritableJSONField(serializers.WritableField):
    def to_native(self, obj):
        return obj


class MyModelSerializer(serializers.HyperlinkedModelSerializer):
    my_json_field = WritableJSONField() # you need this.

Solution 3 - Python

serializers.WritableField is deprecated. This works:

from rest_framework import serializers
from website.models import Picture


class PictureSerializer(serializers.HyperlinkedModelSerializer):
    json = serializers.SerializerMethodField('clean_json')

    class Meta:
        model = Picture
        fields = ('id', 'json')
    
    def clean_json(self, obj):
        return obj.json

Solution 4 - Python

If and only if you know the first-level style of your JSON content (List or Dict), you can use DRF builtin DictField or ListField.

Ex:

class MyModelSerializer(serializers.HyperlinkedModelSerializer):
    my_json_field = serializers.DictField()

It works fine, with GET/PUT/PATCH/POST, including with nested contents.

Solution 5 - Python

Mark Chackerian script didn't work for me, I'd to force the json transform:

import json

class JSONSerializerField(serializers.Field):
    """ Serializer for JSONField -- required to make field writable"""

    def to_internal_value(self, data):
        json_data = {}
        try:
            json_data = json.loads(data)
        except ValueError, e:
            pass
        finally:
            return json_data
    def to_representation(self, value):
        return value

Works fine. Using DRF 3.15 and JSONFields in Django 1.8

Solution 6 - Python

For the record, this "just works" now if you are using PostgreSQL, and your model field is adjango.contrib.postgres.JSONField.

I'm on PostgreSQL 9.4, Django 1.9, and Django REST Framework 3.3.2.

I have previously used several of the other solutions listed here, but was able to delete that extra code.

Example Model:

class Account(models.Model):
    id = UUIDField(primary_key=True, default=uuid_nodash)
    data = JSONField(blank=True, default="")

Example Serializer:

class AccountSerializer(BaseSerializer):
    id = serializers.CharField()
    class Meta:
        model = Account
        fields = ('id','data')

Example View:

class AccountViewSet(
    viewsets.GenericViewSet,
    mixins.CreateModelMixin,      
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin
): 
    model = Account
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    filter_fields = ['id', 'data']

Solution 7 - Python

Thanks by the help. This is the code i finally use for render it

class JSONSerializerField(serializers.Field):
    """Serializer for JSONField -- required to make field writable"""

    def to_representation(self, value):
        json_data = {}
        try:
            json_data = json.loads(value)
        except ValueError as e:
            raise e
        finally:
            return json_data

    def to_internal_value(self, data):
        return json.dumps(data)

class AnyModelSerializer(serializers.ModelSerializer):
    field = JSONSerializerField()

    class Meta:
        model = SomeModel
        fields = ('field',)

Solution 8 - Python

If you're using mysql (haven't tried with other databases), using both DRF's new JSONField and Mark Chackerian's suggested JSONSerializerField will save the json as a {u'foo': u'bar'} string. If you rather save it as {"foo": "bar"}, this works for me:

import json

class JSONField(serializers.Field):
    def to_representation(self, obj):
        return json.loads(obj)

    def to_internal_value(self, data):
        return json.dumps(data)

Solution 9 - Python

> DRF gives us inbuilt field 'JSONField' for binary data, but JSON > payload is verified only when you set 'binary' flag True then it convert into utf-8 and load the JSON payload, else it only > treat them as string(if invalid json is sent) or json and validate both without error even though you cretaed JSONField

class JSONSerializer(serializers.ModelSerializer):
    """
    serializer for JSON
    """
    payload = serializers.JSONField(binary=True)

Solution 10 - Python

To serialize a data from a request you can use the serializers.ModelSerializer

serializers.py

from rest_framwork import serializers
class FinalSerializer(serializers.ModelSerializer):
class Meta:
    model=Student
    fields='__all__'

views.py

import io
from yourappname.serializers import FinalSerializer #replace your app name
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser,MultiPartParser,FormParser
from rest_framework.response import Response


class DataList(APIView):


    parser_classes = (JSONParser,MultiPartParser,FormParser) #If you are using postman
    renderer_classes = (JSONRenderer,)
    #Serialize
    def get(self,request,format=None):
        all_data=Student.objects.all()
        serializer=FinalSerializer(all_data,many=True)
        return Response(serializer.data)#Will return serialized json data,makes sure you have data in your model
    #Deserialize
    #Not tried this function but it will work
    #from django documentation
    def djson(self,request,format=None):
        stream = io.BytesIO(json)
        data = JSONParser().parse(stream)
        serializer = FinalSerializer(data=data)
        serializer.is_valid()
        serializer.validated_data

Solution 11 - Python

If you want JSONField for mysql this is done in django-mysql and serializer was fixed some day ago [1], is not yet in any release.

[1] https://github.com/adamchainz/django-mysql/issues/353

#setting.py add:

    'django_mysql',

#models.py

from django_mysql.models import JSONField

class Something(models.Model):
(...)
    parameters = JSONField()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTzachView Question on Stackoverflow
Solution 1 - PythonMark ChackerianView Answer on Stackoverflow
Solution 2 - PythongzeroneView Answer on Stackoverflow
Solution 3 - PythonDavid DehghanView Answer on Stackoverflow
Solution 4 - PythonJocelyn delalandeView Answer on Stackoverflow
Solution 5 - PythonjonalvarezzView Answer on Stackoverflow
Solution 6 - PythonScott SmithView Answer on Stackoverflow
Solution 7 - PythonantikytheratonView Answer on Stackoverflow
Solution 8 - PythonDaniel LevinsonView Answer on Stackoverflow
Solution 9 - PythonDeepakView Answer on Stackoverflow
Solution 10 - PythonCrazy-Kaleidoscope-7View Answer on Stackoverflow
Solution 11 - PythonSérgioView Answer on Stackoverflow