Django Rest 框架和 JSONField
Posted
技术标签:
【中文标题】Django Rest 框架和 JSONField【英文标题】:Django Rest Framework and JSONField 【发布时间】:2014-04-21 12:10:40 【问题描述】:给定一个带有JSONField 的Django 模型,使用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 对象中的所有浮点数都变成了字符串。
【问题讨论】:
【参考方案1】:如果您使用的是 Django Rest Framework >= 3.3,则 JSONField 序列化程序为 now included。现在这是正确的方法。
如果您使用的是 Django Rest Framework
如果您使用的是 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()
而且,如果你确实需要序列化二进制数据,你可以随时复制official release code
【讨论】:
【参考方案2】:在 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.
【讨论】:
我建议不要使用 JSONField 作为类名,因为它会与其他类发生冲突。 fixmycode,感谢您对类名的建议。嗯...,这只是一个示例代码,它从gist.github.com/rouge8/5445149 复制而来,因此您可以随意更改它。但是@Tzach,'JSONFieldSerializer' 不是我认为的'serializers.WritableField' 子类的合适名称。看起来 JSONFieldSerializer 和 MyModelSerializer 是一样的东西。希望听到您的意见。 感谢@gzerone 的评论。我承认我并没有过多关注新的班级名称。只是希望它不会引起冲突。JSONWritableField
听起来更好吗?
或 WritableJSONField, ;)
这仅对 Django Rest Framework 版本 2 有效。对于版本 3,请参阅 Mark Chackerian 的回答。【参考方案3】:
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
【讨论】:
是的。如果你有一个想要序列化的对象,你只需要阅读它。这不会修改您正在序列化的原始对象。【参考方案4】:当且仅当您知道 JSON 内容的一级样式(List 或 Dict)时,您可以使用内置的 DRF DictField 或 ListField。
例如:
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.【参考方案5】: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 吗? 我使用serializers.Serializer
,但ModelSerializer
也应该可以使用
请注意,这确实适用于具有 DRF 3.2 的 ModelSerializer
。【参考方案6】:
郑重声明,如果您使用的是 PostgreSQL,并且您的模型字段是 adjango.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']
【讨论】:
【参考方案7】:感谢您的帮助。这是我最终用来渲染它的代码
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',)
【讨论】:
【参考方案8】:如果您使用的是 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)
【讨论】:
【参考方案9】:DRF 为我们提供了二进制数据的内置字段“JSONField”,但 JSON 仅当您将“二进制”标志设置为 True 时才验证有效负载,然后将其转换为 utf-8 并加载 JSON 有效负载,否则仅 将它们视为字符串(如果发送了无效的 json)或 json 并验证两者 即使您创建了 JSONField 也没有错误
class JSONSerializer(serializers.ModelSerializer):
"""
serializer for JSON
"""
payload = serializers.JSONField(binary=True)
【讨论】:
【参考方案10】:要从请求中序列化数据,您可以使用 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
【讨论】:
【参考方案11】:如果你想要 mysql 的 JSONField,这是在 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()
【讨论】:
以上是关于Django Rest 框架和 JSONField的主要内容,如果未能解决你的问题,请参考以下文章
如何在 django rest 框架中的 postgres JSONField 上应用过滤器?
如何在具有 Jsonfield 的模型中发布 django rest 中的数据
CVE-2019-14234 Django JSONField SQL注入漏洞复现
Django / PostgresQL jsonb (JSONField) - 将选择和更新转换为一个查询