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.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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
Method 2
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.
Method 3
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
Method 4
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.
Method 5
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
Method 6
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']
Method 7
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',)
Method 8
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)
Method 9
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)
Method 10
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
Method 11
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()
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0