web-dev-qa-db-fra.com

Django Rest Framework et JSONField

É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.

46
Tzach

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

58
Mark Chackerian

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.
18
gzerone

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
5
David Dehghan

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

4
jonalvarezz

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']
4
Scott Smith

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é.

4
Jocelyn delalande

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)
1
Daniel Levinson

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',)
1
antikytheraton

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
0
Albin David

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)
0
Deepak

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

setting.py

ajouter: 

    'Django_mysql',

models.py

from Django_mysql.models import JSONField

class Something(models.Model):
(...)
    parameters = JSONField()
0
Sérgio