1. 程式人生 > >Django model,QuerySet 序列化成json的方法(解決了500 (INTERNAL SERVER ERROR)的問題)

Django model,QuerySet 序列化成json的方法(解決了500 (INTERNAL SERVER ERROR)的問題)

 

問題描述:ajax從前端向後端請求資料,後端的檢視函式需要把資料轉換成json格式傳過去。但是一直報下面這個500的錯誤:

經過研究發現,拼湊json串的時候將一組queryset格式的資料拼進去了,如下:

{'otherlog_success': 57772481L,
 'attributetable_failed': 0L,
 'attributetable_success': 316222921L, 
 u'id': 61L, 
 'Spam_whitelistfilter': 25283492L,
 'Spam_success': 89939885L, 
 'date': u'2018-11-02 00:00:00'}

可以看到queryset格式與標準的json格式還是有寫些細微的差別的。

啥也別說了,解決問題吧!

感謝文章:http://www.yihaomen.com/article/python/279.htm

提到序列化與反序列化,通常會想到 json ,xml .在J2EE的開發中,這是很常用的技術,比如一個java class與xml之間的序列化與反序列化,我們可以通過 xstream來實現,如果是與json之間的轉換,我們可以通過 gson.jar或者jsonlib.jar 來實現。方法很多,也是常見的方法。

但在python 中,我們常用的是json 的序列化,python2.7 已經包含了json package,這個也是從simplejson 基礎上改變而來。這個json 包主要提供了dump,load 來實現dict 與 字串之間的序列化與反序列化,這很方便的可以完成,可以參考這篇文章

python json。但現在的問題是,這個json包不能序列化 django 的models 裡面的物件的例項。
經過分析,網路搜尋,發現有如下解決方案.

利用 from django.core import serializers 的方法實現

from django.core import serializers
data = serializers.serialize("json", SomeModel.objects.all())
data1 = serializers.serialize("json", SomeModel.objects.filter(myfield1=myvalue))

上面兩個是沒有問題的,因為序列化的物件是 Queryset, 因此是成功的。但如果是用SomeModel.objects.get(id=myid) 得到一個具體的例項的時候,問題就來了

data = serializers.serialize("json", SomeModel.objects.get(id=myid))

肯定會出現如下錯誤:

for obj in queryset:
TypeError: 'SomeModel' object is not iterable

一看錯誤就知道,因為  SomeModel.objects.get(id=myid) 返回的是一個具體的例項,而不是一個集合物件,因此是不可以 iterable 的。所以報錯。

從上面的分析可以看出  django的 serializers 只支援 queryset,而不支援model的例項,那麼怎麼實現呢?
1.我們自己把這個單個物件模擬成一個集合,然後去掉前後的"[""]"符號,就可以了。

from django.utils import simplejson
from django.db import models
from django.core.serializers import serialize,deserialize
from django.db.models.query import QuerySet
from django.test import TestCase

class MyEncoder(simplejson.JSONEncoder):
     """ 繼承自simplejson的編碼基類,用於處理複雜型別的編碼
     """
     def default(self,obj):
             if isinstance(obj,QuerySet):
                 """ Queryset例項
                 直接使用Django內建的序列化工具進行序列化
                 但是如果直接返回serialize('json',obj)
                 則在simplejson序列化時會被從當成字串處理
                 則會多出前後的雙引號
                 因此這裡先獲得序列化後的物件
                 然後再用simplejson反序列化一次
                 得到一個標準的字典(dict)物件
                 """
                 return simplejson.loads(serialize('json',obj))
             if isinstance(obj,models.Model):
                 """
                 如果傳入的是單個物件,區別於QuerySet的就是
                 Django不支援序列化單個物件
                 因此,首先用單個物件來構造一個只有一個物件的陣列
                 這是就可以看做是QuerySet物件
                 然後此時再用Django來進行序列化
                 就如同處理QuerySet一樣
                 但是由於序列化QuerySet會被'[]'所包圍
                 因此使用string[1:-1]來去除
                 由於序列化QuerySet而帶入的'[]'
                 """
                 return simplejson.loads(serialize('json',[obj])[1:-1])
             if hasattr(obj, 'isoformat'):
                 #處理日期型別
                 return obj.isoformat()
             return simplejson.JSONEncoder.default(self,obj)

def jsonBack(json):
     """    進行Json字串的反序列化
         一般來說,從網路得回的POST(或者GET)
         引數中所包含json資料
         例如,用POST傳過來的引數中有一個key value鍵值對為
         request.POST['update']
         = "[{pk:1,name:'changename'},{pk:2,name:'changename2'}]"
         要將這個value進行反序列化
         則可以使用Django內建的序列化與反序列化
         但是問題在於
         傳回的有可能是代表單個物件的json字串
         如:
         request.POST['update'] = "{pk:1,name:'changename'}"
         這是,由於Django無法處理單個物件
         因此要做適當的處理
         將其模擬成一個數組,也就是用'[]'進行包圍
         再進行反序列化
     """
     if json[0] == '[':
         return deserialize('json',json)
     else:
         return deserialize('json','[' + json +']')

def getJson(**args):
     """    使用MyEncoder這個自定義的規則類來序列化物件
     """
     result = dict(args)
     return simplejson.dumps(result,cls=MyEncoder)

在上面的例子中,自定義了一個序列化規則類MyEncoder,用來處理集合或者集合物件,然後實現了一個可變引數的工具方法getJson,用於傳入多個引數,並將其一同序列化。另外還有一個反序列化物件的方法jsonBack,接受一個代表物件或者物件集合的json而返回一個物件集合。這樣一來就可以很好的使用配合SimpleJson和Django來完成序列化工作了

2.直接利用python 2.7 提供的json包,或者用simplejson都可以
首先,你需要在django model的定義中增加一個方法toJSON,利用了django model  能訪問 _meta.fields 得到相關屬性而得到,例子如下:

class Category(models.Model):
    autoid = models.AutoField(primary_key=True)
    email=models.CharField(max_length=150,blank=False)
    comtype=models.CharField(max_length=20,blank=False)
    catname=models.CharField(max_length=150,blank=False)  
    
    def __unicode__(self):
        return '%s' % (self.catname)
    
    def toJSON(self):
        import json
        return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))

現在用django查出資料,並轉換成json

row=models.Category.objects.get(autoid=23)    
print row.toJSON()

你會發現,成功轉換了。當然,這個toJSON方法,如果要求可讀性比較好的話,可以這樣寫

def toJSON(self):
    fields = []
    for field in self._meta.fields:
        fields.append(field.name)

    d = {}
    for attr in fields:
        d[attr] = getattr(self, attr)

    import json
    return json.dumps(d)