Django Rest Framework和JSONField

70

如果有一个包含JSONField的 Django 模型,那么使用Django Rest Framework 序列化和反序列化 JSONField 的正确方式是什么?

我已经尝试过创建自定义的serializers.WritableField并覆盖to_nativefrom_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对象中的所有浮点数都变成了字符串。

11个回答

82
如果您正在使用Django Rest Framework >=3.3,则JSONField序列化程序现在已包含。这是正确的方法。
如果您使用的是DRF < 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

然后像这样将该字段添加到模型中:

And then add in the field into the model like

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

而且,如果您确实需要序列化二进制数据,您始终可以复制官方发布的代码


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

3
我建议不要使用JSONField作为类名,因为它会与其他类冲突。 - fixmycode
fixmycode,感谢您对类名的建议。嗯...那只是一份示例代码,复制自https://gist.github.com/rouge8/5445149,所以您可以随意更改它。但@Tzach,“JSONFieldSerializer”不是“serializers.WritableField”的子类命名合适。看起来JSONFieldSerializer和MyModelSerializer是同样的东西。期待听到您的意见。 - gzerone
1
感谢@gzerone的评论。我承认我没有太关注新类名。只是希望它不会引起冲突。你觉得JSONWritableField听起来更好吗? - Tzach
可写的JSON字段,;) - gzerone
6
这仅适用于Django Rest Framework 2版本。有关第3版,请参阅Mark Chackerian的答案。 - mhsmith
是的,自从Django 1.8和DRF 3.x以来,我在我的项目中用ListField或DictField替换了WritableJSONField,这对于Postgres数组字段运作良好。 - gzerone

6

只有当您知道 JSON 内容的第一级样式(列表或字典)时,才可以使用 DRF 内置的 DictFieldListField

例如:

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

它可以很好地运行,支持GET/PUT/PATCH/POST,包括嵌套内容。


以上方法都不适用于我,但这个可以。奇怪的是它在列表中排名较低。在客户端,我必须使用PATCH传递 { "field": JSON.stringify(data), ... }。默认的jquery $.ajax调用会将每个值拆分成长键,如“field[key1][key2][etc]”: value。 - Scott Smith

5

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

是的。如果您有一个要序列化的对象,您只需要读取它。这不会修改您正在序列化的原始对象。 - David Dehghan

4
值得一提的是,如果您正在使用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']

4
马克·查克里安(Mark Chackerian)的脚本对我没用,我不得不强制进行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

很好用。在Django 1.8中使用DRF 3.15和JSONFields。


你如何将这个序列化器字段与模型的JSONFields集成?你使用ModelSerializer吗? - spacediver
我使用 serializers.Serializer,但是使用 ModelSerializer 也可以。 - jonalvarezz
请注意,这适用于带有DRF 3.2的ModelSerializer - Beau

2

感谢您的帮助。这是我最终用于渲染它的代码。

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
DRF为我们提供了内置字段“JSONField”用于二进制数据,但仅在您将“binary”标志设置为True时才验证JSON有效负载,然后将其转换为utf-8并加载JSON有效负载,否则它仅将其视为字符串(如果发送的是无效json)或json,并验证两者而不出错即使您创建了JSONField。
class JSONSerializer(serializers.ModelSerializer):
    """
    serializer for JSON
    """
    payload = serializers.JSONField(binary=True)

1
如果您正在使用mysql(尚未尝试其他数据库),同时使用DRF的新JSONField和Mark Chackerian建议的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)

1

要将请求中的数据序列化,您可以使用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接