Django モデルオブジェクトを何としてでも JSON に変換する

今回は説明を省略していくので注意。


あなたは models.py にモデルを定義しているとします。

#
# models.py
#
from django.db import models

class Item(models.Model):
    name = CharField(max_length=50, primary_key=True)
    tags = models.ManyToManyField(Tag)    # 別途 Tag モデルが定義されているとする

djangoModel インスタンスJSON 化する場合、以下の方法(ドキュメンテーション通り)なら(一応)問題はありません。
filter() メソッドが返す QuerySetJSON 化しやすい。
ただし、serializers が生成する JSON では 'pk', 'fields' 等の変なキーを使わないといけない

from django_project.application import models    # models.py
from django.core import serializers

items = models.Item.objects.filter(name='name')    # QuerySet
# JSON
json_serializer = serializers.get_serializer('json')()
data = simplejson.dumps(items, ensure_ascii=False)


一方、get() メソッドや for 文により得られた Model インスタンスは、上記の方法ではうまく JSON 化できません
Item is not JSON serializable」で何度も苦しめられました。
複数の Model辞書リストに入れて JSON 化したい場合、これでは困ります(もちろん、直接 Model インスタンスJSON 化する場合も)。


解決策として、独自にエンコード用の関数を定義する必要があるようです。
また、serializers ではなく simplejson モジュールを直接使用します。
変換関数は simplejson.dumps()default 引数で指定できます。

from django_project.application import models    # models.py
from django.db import models as django_models    # django が提供する models モジュール
from django.utils import simplejson

item = models.Item.objects.get(name='name')    # Item モデル

data = simplejson.dumps(item, ensure_ascii=False, default=encode_myway)
return HttpResponse(data, mimetype='application/json')

# 独自の変換関数    
def encode_myway(obj):
    if isinstance(obj, django_models.Model):
        return obj.encode()    # models.py のモデルに encode() メソッドを追加定義すること
        # encode という名前は適当に付けました
    elif isinstance(obj, QuerySet):
        return list(obj)    # 空の QuerySet は list 化する
    else:
        raise TypeError(repr(obj) + " is not JSON serializable")

空の QuerySet は「TypeError: [] is not JSON serializable」となるため、上記のように list 化します。


変換用の encode() メソッドを models.py の各モデルクラスに定義します。

#
# models.py
#
from django.db import models

class Item(models.Model):
    name = CharField(max_length=50, primary_key=True)
    tags = models.ManyToManyField(Tag)    # 別途 Tag モデルが定義されているとする
    
    def encode(self):
         return {
             'name': self.name,
             'tags': self.tags.all(),    # ManyToMany 等は all() でエンティティ化(非 QuerySet 化)
             }
    # JSON にキーが必要ないなら return [self.name, self.tags.all()] としてもよい。

これで Model インスタンスを自由に JSON 化できます。
私は Model を含んだ辞書リスト [{'item': item}, ... ] を view.pyJSON 化した Response を作ります。

  • 参考

Django models are not ajax serializable - Stack Overflow