Example of Django model serialized to json

  • 2020-12-16 06:03:08
  • OfStack

In this paper, the environment

Python 3.6.5 Django 2.0.4

fix (2018.5.19) : I recently learned that the model base class of Django needs to be declared as abstract, so I added the abstract declaration in the original code to avoid misleading

In Django, how do you serialize the model class to json

Convert the model class to a dictionary, then convert to json using the dumps method of the json library

I won't go into the first method, but just go to the official documents

In general, the officially provided methods should be relatively easy to use and stable, however, using the official serializers is problematic:

The format is ugly. The format is as follows:


[
  {
    "pk": "4b678b301dfd8a4e0dad910de3ae245b",
    "model": "sessions.session",
    "fields": {
      "expire_date": "2013-01-16T08:16:59.844Z",
      ...
    }
  }
]

Yes, where pk refers to the default primary key, model refers to the model type of that object, and then fields is the various fields of obj... Really do not know how to evaluate

Does not support list very well Not very friendly to 1 foreign key (including ManyToManyField, etc.) Even the DateField itself is not well supported

Counting one of the official serializers' shortcomings, of course, there must be a solution to some of the above points, but ah, I really don't want to do a whine.

So throw out my solution:

New class 1 BaseModel, such inheritance in django. Official model db. models. Model In each BaseModel, declare a method that generates a dictionary about the object Use this object dictionary to generate json

The strategy for generating object dictionaries is as follows:

Get all the field names for this object by reflection Gets a field field based on the field name If filed is of type int, float, str, put it directly in the dictionary as "field name ": field value If the type of field is datetime or date, use date and put it in the dictionary If field is of type BaseModel, then call the getDict method of that field to recursively obtain the dictionary corresponding to that field and put it in the dictionary If the type of field is ManyToMany, we use the all method of this field to get all object of this field in the specific grass species, and then also by the getDict method to put it into the dictionary

Source code and usage


from django.db import models
import json


class BaseModel(models.Model):
  class Meta:
    abstract = True

  #  return self._meta.fields Is not, but is again a list of required field names 
  #  like ['name','type']
  def getMtMField(self):
    pass

  #  Return is required in the json A list of field names that are ignored in 
  #  like ['password']
  def getIgnoreList(self):
    pass

  def isAttrInstance(self, attr, clazz):
    return isinstance(getattr(self, attr), clazz)

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

    d = {}
    import datetime
    for attr in fields:
      if isinstance(getattr(self, attr), datetime.datetime):
        d[attr] = getattr(self, attr).strftime('%Y-%m-%d %H:%M:%S')
      elif isinstance(getattr(self, attr), datetime.date):
        d[attr] = getattr(self, attr).strftime('%Y-%m-%d')
      #  Special handling datetime The data of 
      elif isinstance(getattr(self, attr), BaseModel):
        d[attr] = getattr(self, attr).getDict()
      #  Recursive generation BaseModel Of the class dict
      elif self.isAttrInstance(attr, int) or self.isAttrInstance(attr, float) \
          or self.isAttrInstance(attr, str):
        d[attr] = getattr(self, attr)
      # else:
      #   d[attr] = getattr(self, attr)

    mAttr = self.getMtMField()
    if mAttr is not None:
      for m in mAttr:
        if hasattr(self, m):
          attlist = getattr(self, m).all()
          l = []
          for attr in attlist:
            if isinstance(attr, BaseModel):
              l.append(attr.getDict())
            else:
              dic = attr.__dict__
              if '_state' in dic:
                dic.pop('_state')
              l.append(dic)
          d[m] = l
    #  Due to the ManyToMany Class cannot exist in _meat.fields , so subclasses need to be in getMtMFiled Returns these fields 
    if 'basemodel_ptr' in d:
      d.pop('basemodel_ptr')

    ignoreList = self.getIgnoreList()
    if ignoreList is not None:
      for m in ignoreList:
        if d.get(m) is not None:
          d.pop(m)
    #  Remove unwanted fields 
    return d

  def toJSON(self):
    import json
    return json.dumps(self.getDict(), ensure_ascii=False).encode('utf-8').decode()

Usage:

All classes of models inherit from the BaseModel class and then call the toJSON() method of this class

Note that for some reason the ES97en. _meta. fields does not contain the ManyToManyField field, so you need to override the getMtMField method. Examples are as follows:


class Book(BaseModel):
  name = models.CharField(max_length=50)
  authors = models.ManyToManyField(Author)
  publish = models.ForeignKey(Publisher, on_delete=models.SET_NULL, blank=True, null=True)
  page = models.IntegerField(default=0) #  Number of pages 
  introduction = models.CharField(max_length=500)
  bookType = models.ManyToManyField(BookType, null=True, blank=True)
  bookTag = models.ManyToManyField(BookTag, null=True, blank=True)
  evaluation = models.FloatField()
  coverUrl = models.CharField(max_length=100, null=True, blank=True)

  def getMtMField(self):
    return ['bookType', 'bookTag']

Results:


{
  "id":4,
  "name":"Django From entry to abandonment ",
  "page":123,
  "introduction":"introduction",
  "evaluation":1,
  "bookType":[
    {
      "id":1,
      "name":" type "
    }
  ],
  "bookTag":[
    {
      "id":2,
      "name":"tag"
    }
  ]
}

Afterword.

Source has a reference, that is, getDict method in the first for loop, but I don't bother to find the original link, please forgive me, I hereby declare;

I python novice, code is not standard, I hope you understand; The code is not perfect, but I hope it can help you.

Related articles: