Django Rest Framework и JSONField
Принимая во внимание модель Django с JSONField, каков правильный способ ее сериализации и десериализации с использованием Django Rest Framework?
Я уже пытался создать кастом serializers.WritableField
и переопределение to_native
а также 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)
Но когда я пытаюсь обновить модель с помощью partial=True
все поплавки в объектах JSONField становятся строками.
10 ответов
Если вы используете Django Rest Framework >= 3.3, то теперь включен сериализатор JSONField. Теперь это правильный путь.
Если вы используете Django Rest Framework < 3.0, посмотрите ответ gzerone.
Если вы используете DRF 3.0 - 3.2 И не можете обновить И вам не нужно сериализовать двоичные данные, следуйте этим инструкциям.
Сначала объявите класс поля:
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
А затем добавить в поле в модель, как
class MySerializer(serializers.ModelSerializer):
json_data = JSONSerializerField()
И, если вам нужно сериализовать двоичные данные, вы всегда можете скопировать официальный код выпуска
В 2.4.х:
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.
Если и только если вы знаете стиль вашего уровня JSON первого уровня (List или Dict), вы можете использовать встроенный в DRF DictField или ListField.
Пример:
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = serializers.DictField()
Работает нормально, с GET/PUT/PATCH/POST
в том числе с вложенным содержимым.
serializers.WritableField устарела. Это работает:
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
Скрипт Марка Чакериана у меня не сработал, я бы принудительно преобразовал 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
Работает отлично. Использование DRF 3.15 и JSONFields в Django 1.8
Для справки, это "просто работает" сейчас, если вы используете PostgreSQL, и ваше поле моделиdjango.contrib.postgres.JSONField
,
Я использую PostgreSQL 9.4, Django 1.9 и Django REST Framework 3.3.2.
Ранее я использовал несколько других решений, перечисленных здесь, но смог удалить этот дополнительный код.
Пример модели:
class Account(models.Model):
id = UUIDField(primary_key=True, default=uuid_nodash)
data = JSONField(blank=True, default="")
Пример сериализатора:
class AccountSerializer(BaseSerializer):
id = serializers.CharField()
class Meta:
model = Account
fields = ('id','data')
Пример просмотра:
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']
Спасибо за помощь. Это код, который я наконец-то использую для его рендеринга
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',)
Если вы используете mysql (не пробовал с другими базами данных), используйте оба новых DRF JSONField
и Марк Чакериан предложил JSONSerializerField
сохранит JSON как {u'foo': u'bar'}
строка. Если вы предпочитаете сохранить его как {"foo": "bar"}
, это работает для меня:
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)
Для сериализации данных из запроса вы можете использовать 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 дает нам встроенное поле "JSONField" для двоичных данных, но полезная нагрузка JSON проверяется только тогда, когда вы устанавливаете флаг "двоичный" True, затем он конвертируется в utf-8 и загружает полезную нагрузку JSON, иначе он обрабатывает их только как строку (если недопустимый json отправлено) или json и подтвердите оба без ошибок, даже если вы указали JSONField
class JSONSerializer(serializers.ModelSerializer):
"""
serializer for JSON
"""
payload = serializers.JSONField(binary=True)
Если вы хотите JSONField для mysql, это делается в django-mysql, и сериализатор был исправлен несколько дней назад [1], но еще не выпущен.
[1] https://github.com/adamchainz/django-mysql/issues/353
setting.py
добавлять:
'django_mysql',
models.py
from django_mysql.models import JSONField
class Something(models.Model):
(...)
parameters = JSONField()