【问题标题】:Django Rest Framework and JSONFieldDjango Rest 框架和 JSONField
【发布时间】:2014-04-21 12:10:40
【问题描述】:

给定一个带有JSONField 的Django 模型,使用Django Rest Framework 对其进行序列化和反序列化的正确方法是什么?

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

【问题讨论】:

    标签: python django django-models django-rest-framework


    【解决方案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 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.
      【解决方案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()
                

                【讨论】:

                  猜你喜欢
                  • 2018-08-02
                  • 2016-08-09
                  • 2015-01-04
                  • 2015-08-07
                  • 2015-04-26
                  • 2015-07-16
                  • 2020-07-27
                  相关资源
                  最近更新 更多