Peewee model to JSON

Question:

I’m creating an API using peewee as the ORM and I need the ability to convert a peewee model object into a JSON object to send to the user. Does anyone know of a good way to do this?

Asked By: shoopdelang

||

Answers:

Peewee has a model_to_dict and dict_to_model helpers in the playhouse.shortcuts extension module.

You could use these as follows:

from playhouse.shortcuts import model_to_dict, dict_to_model

user_obj = User.select().where(User.username == 'charlie').get()
json_data = json.dumps(model_to_dict(user_obj))

Also note that model_to_dict() can recurse through related models, include back-referenced models, and exclude certain fields from being serialized.

Answered By: coleifer

you can do something like that:

class MyModel(peewee.Model):

  def __str__(self):
    r = {}
    for k in self._data.keys():
      try:
         r[k] = str(getattr(self, k))
      except:
         r[k] = json.dumps(getattr(self, k))
    return str(r)


class User(MyModel):
    email = CharField()
    status = CharField(default="enabled")
    firstname = CharField()
    lastname = CharField()
    class Meta:
        database = db
Answered By: Ali SAID OMAR

also, you can get model as a dict, and then convert to json with correct field types (bool, int, float, etc.):

import peewee
import json
from bson import json_util
from datetime import datetime

class User(peewee.Model):
    email = CharField()
    status = BooleanField(default=True)
    firstname = CharField()
    lastname = CharField()
    age = IntegerField()
    created = DateTimeField(default=datetime.now())
    class Meta:
        database = db

user = User.select().dicts().get()
print json.dumps(user, default=json_util.default)
Answered By: kiba

I usually implement the model to dict and dict to model functions, for maximum security and understanding of the inner workings of the code. Peewee does a lot of magic and you want to be in control over it.

The most obvious argument for why you should not iterate on the fields but rather explicitly specify them is because of security considerations. Not all fields can be exposed to the user, and I assume you need this functionality to implement some sort of REST API.

So – you should do something like this:

class UserData(db.Model):
    user = db.ForeignKeyField(User)
    data = db.CharField()

    def serialize():
        # front end does not need user ID here
        return {
            'data': self.data
        }

    @classmethod
    def from_json(cls, json_data):
        UserData.create(
            # we enforce user to be the current user
            user=current_user,
            data=json_data['data']
        )
Answered By: Ron Reiter

I had this very same problem and ended up defining my own parser extension for JSON types that could not be automatically serialized. I’m fine for now in using strings as data represented (although you could possibly use different datatypes, but beware of approximation using floating points!

In the following example, I put this in a file called json_serialize.py inside a utils folder:

from decimal import Decimal
import datetime

try:
    import uuid
    _use_uuid = True
except ImportError:
    _use_uuid = False

datetime_format = "%Y/%m/%d %H:%M:%S"
date_format = "%Y/%m/%d"
time_format = "%H:%M:%S"


def set_datetime_format(fmt_string):
    datetime_format = fmt_string


def set_date_format(fmt_string):
    date_format = fmt_string


def set_time_format(fmt_string):
    time_format = fmt_string


def more(obj):
    if isinstance(obj, Decimal):
        return str(obj)

    if isinstance(obj, datetime.datetime):
        return obj.strftime(datetime_format)

    if isinstance(obj, datetime.date):
        return obj.strftime(date_format)

    if isinstance(obj, datetime.time):
        return obj.strftime(time_format)

    if _use_uuid and isinstance(obj, uuid.UUID):
        return str(obj.db_value())

    raise TypeError("%r is not JSON serializable" % obj)

Then, in my app:

import json
from utils import json_serialize

...


json.dumps(model_to_dict(User.get()), default=json_serialize.more)

edit just to add: this is very largely inspired by json_utils.default module found in mongodb but mainly relies on the json module and needs no import of mongodb own bson/json_utils module.

Usually I update it to support new types as soon as my app raises the TypeError for it found a type not able to serialize

Answered By: Matteo Furlan

when single fetch

user = User.select().where(User.id == 1).get()
model_to_dict(user)  #to Dict

when Multiple fetch

users = list(User.select().where(User.name ** 'a%').dicts()) 
Answered By: Pramit Sawant

For anybody having issues like TypeError: Object of type date is not JSON serializable, this works for me (tested on Python 3.8.2).

from playhouse.shortcuts import model_to_dict
import json

def print_model(model):
    print(json.dumps(model_to_dict(model), indent=4, sort_keys=True, default=str))

def print_models(models):
    print(json.dumps(list(models.dicts()), indent=4, sort_keys=True, default=str))

Usage 1 – Single model

for person in Person.select():
    print_model(person)

Usage 2 – Many models

print_models(Person.select())
Answered By: AndreFeijo
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.