Étant donné un modèle Django avec JSONField , quelle est la bonne façon de le sérialiser et de le désérialiser à l'aide de Django Rest Framework ?
J'ai déjà essayé de créer un serializers.WritableField
personnalisé et de remplacer to_native
et 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)
Mais lorsque j'essaie de mettre à jour le modèle à l'aide de partial=True
, tous les objets flottants dans les objets JSONField deviennent des chaînes.
Si vous utilisez Django Rest Framework> = 3.3, le sérialiseur JSONField est maintenant inclus . C'est maintenant la bonne façon.
Si vous utilisez Django Rest Framework <3.0, consultez la réponse de gzerone.
Si vous utilisez DRF 3.0 - 3.2 ET que vous ne pouvez pas mettre à niveau ET qu'il n'est pas nécessaire de sérialiser des données binaires, suivez ces instructions.
Commencez par déclarer une classe de champ:
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
Et puis ajoutez dans le champ dans le modèle comme
class MySerializer(serializers.ModelSerializer):
json_data = JSONSerializerField()
Et, si vous avez besoin de sérialiser des données binaires, vous pouvez toujours copier code de version officiel
En 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.
serializers.WritableField est obsolète. Cela marche:
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
Le script Mark Chackerian ne fonctionnait pas pour moi, je devais forcer la transformation json:
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
Fonctionne bien. Utiliser DRF 3.15 et JSONFields dans Django 1.8
Pour mémoire, cela "fonctionne" maintenant si vous utilisez PostgreSQL et que votre champ de modèle est un Django.contrib.postgres.JSONField
.
Je suis sur PostgreSQL 9.4, Django 1.9 et Django REST Framework 3.3.2.
J'ai déjà utilisé plusieurs des solutions énumérées ici, mais j'ai pu supprimer ce code supplémentaire.
Exemple de modèle:
class Account(models.Model):
id = UUIDField(primary_key=True, default=uuid_nodash)
data = JSONField(blank=True, default="")
Exemple de sérialiseur:
class AccountSerializer(BaseSerializer):
id = serializers.CharField()
class Meta:
model = Account
fields = ('id','data')
Exemple de vue:
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']
Si et seulement si vous connaissez le style de premier niveau de votre contenu JSON (List ou Dict), vous pouvez utiliser le format DRF intégré DictField ou ListField .
Ex:
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = serializers.DictField()
Cela fonctionne très bien avec GET/PUT/PATCH/POST
, y compris avec le contenu imbriqué.
Si vous utilisez mysql (vous n’avez pas essayé avec d’autres bases de données), utiliser le nouveau JSONField
de DRF et le JSONSerializerField
suggéré par Mark Chackerian enregistreront le json sous la forme d'une chaîne {u'foo': u'bar'}
. Si vous préférez le sauvegarder sous le nom {"foo": "bar"}
, cela fonctionne pour moi. :
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)
Merci par l'aide. C'est le code que j'ai finalement utilisé pour le rendre
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',)
Pour sérialiser une donnée d'une requête, vous pouvez utiliser le 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
DRF nous donne le champ incorporé 'JSONField' pour les données binaires, mais JSON La charge utile est vérifiée uniquement lorsque vous activez le drapeau 'binaire' Vrai, puis elle est convertie en utf-8 et chargez la charge JSON, sinon elle uniquement Traitez-les comme une chaîne (si JSON non valide est envoyé) ou JSON et validez les deux sans erreur même si vous avez créé JSONField
class JSONSerializer(serializers.ModelSerializer):
"""
serializer for JSON
"""
payload = serializers.JSONField(binary=True)
Si vous voulez JSONField pour mysql, cela est fait dans Django-mysql et le sérialiseur a été corrigé il y a un jour [1], il ne figure dans aucune version.
[1] https://github.com/adamchainz/Django-mysql/issues/353
ajouter:
'Django_mysql',
from Django_mysql.models import JSONField
class Something(models.Model):
(...)
parameters = JSONField()