1. 程式人生 > 程式設計 >Django 再談一談json序列化

Django 再談一談json序列化

我們知道JSON字串是目前流行的資料交換格式,在pyhton中我們通過json模組,將常用的資料型別轉化為json字串。但是,json支援轉化的資料型別是有限的。

比如,我們通過ORM從資料庫查詢出的結果,試圖通過json序列化:

from .models import UserInfo

def index(request):
  user_list = UserInfo.objects.all()
  import json
  return HttpResponse(json.dumps(user_list))
  # TypeError: Object of type 'QuerySet' is not JSON serializable

報錯,QuerySet不是JSON能序列化的物件。那麼有什麼辦法可以解決呢?

注意,如果是通過values查詢,如UserInfo.objects.values("name"),查詢出來的結果雖然也是QuerySet物件,但是其結構是這樣的:<QuerySet [{'name': 'egon'},{'name': 'sb'}]>, 類似於列表套字典的結構。對於這種情況,我們可以通過list()方法將QuerySet 物件轉化為列表,這樣就可以直接用json.dumps()進行序列化了。

方法一:serializers

def index(request):
  user_list = UserInfo.objects.all()
  from django.core import serializers
  user_list_json = serializers.serialize("json",user_list)
  return HttpResponse(user_list_json)

將返回的結果放到bejson校驗結果如下:

[
 {
  "model": "app01.userinfo","pk": 1,"fields": {
    "name": "egon","pwd": "123"
  }
},{
  "model": "app01.userinfo","pk": 2,"fields": {
    "name": "sb","pwd": "123"
  }
}
]

注:pk代表主鍵(可以是預設的id主鍵欄位,也可以是使用者自定義的主鍵欄位)

觀察序列化結果,發現這種方式將服務端資料庫的表名都暴露了;另外serializers不支援連表序列化,只能拿到另一張表的id。下面我們我們用一種新的方式。

方法二:自定義JSON處理器

檢視json.dumps原始碼,發現序列化時,用到了一個引數cls = JSONEncoder,我們可以繼承它,自定義一個類,重寫它的default方法,來處理我們需要的資料型別。比如自定義對時間物件進行轉化:

import json
from datetime import date
from datetime import datetime

class JsonCustomEncoder(json.JSONEncoder):

  def default(self,field):

    if isinstance(field,datetime):
      return field.strftime('%Y-%m-%d %H:%M:%S')
    elif isinstance(field,date):
      return field.strftime('%Y-%m-%d')
    else:
      return json.JSONEncoder.default(self,field)

下面我們試著序列化一個datetime物件:

def index(request):
  now = datetime.now()
  import json
  return HttpResponse(json.dumps(now,cls=JsonCustomEncoder))

再次訪問http://127.0.0.1:8000/index.html:

Django 再談一談json序列化

補充知識:Django ORM物件Json序列化問題

碰到了一個問題:在使用json.dumps()序列化Django ORM的Queryset物件,傳遞給前端的時候,程式報錯:

Object of type 'QuerySet' is not JSON serializable

在python 中,常用的json 的序列化是從simplejson 基礎上改變而來。這個json 包主要提供了dump,load 來實現dict 與 字串之間的序列化與反序列化,這很方便的可以完成,但現在的問題是,這個json包不能序列化 django 的models 裡面的物件的例項。

經過一番度娘搜尋,發現有如下解決方案:

使用django.core自帶的serializers模組:

#django ORM的 Queryset物件預設無法被直接json.dumps()序列化,django.core提供的serializers模組提供將其序列化成str型別
#的功能,serializers處理後,再次json.dumps傳給前端,前端需要經過兩次json.Parse()處理,才能得到原物件型別,但是格式發
#生了變化,需要按新的方式取索引.例如:obj['pk']取主鍵,obj['fields']["caption"]取obj的caption欄位
由QuerySet:[<Business: Business object>]
變為了:
[{"model": "cmdb.business","fields": {"caption": "develop"}}]

這樣前端就可以正常獲取資料了,只不過此欄位需要兩次json.Parse()處理。

至於使用models.Host.objects.get(id=xx)的方式獲取到單個物件,而非Queryset物件,serializers預設也無法處理的問題,可以自定義json方法來實現dumps序列化

json預設只支援python原生的list、tuple、dict資料型別物件的序列化,若需要擴充套件其他型別物件的序列化功能,可以這樣修改:

import json as default_json
from json.encoder import JSONEncoder


class BaseResponse(object):
  def __init__(self):
    self.status = True
    self.message = None
    self.data = None
    self.error = None
o=BaseResponse()

class JsonCustomEncoder(JSONEncoder):
  def default(self,o):
    if isinstance(o,BaseResponse):
      return o.__dict__
    return JSONEncoder.default(self,o)

o1=json.dumps(o,cls=JsonCustomEncoder)


>>> print(o1)
{"message": null,"error": null,"data": null,"status": true}
>>> print(type(o1))
<class 'str'>
#在序列化時指定cls引數,cls=自定義的序列化類,在自定義序列化類的default方法中判斷,如果是指定的類的例項的話,則將該類轉換成dict格式返回,若指定類的例項,則使用json模組預設的序列化方法。最終得到的return值為str型別。

以上這篇Django 再談一談json序列化就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。