简体   繁体   中英

Flask API throws TypeError: Object of type is not JSON serializable

I am building a Flask API w/a Postgresql db. I have two main models, Contacts and Outlets with a many-to-many association. Everything works fine until I attempt to use the association to return the outlets for a given contact. I am using flask-sqlalchemy as an ORM. The route below aimed at retrieving a single contact throws: TypeError: Object of type Outlet is not JSON serializable

@contacts.get("/<int:id>")
@jwt_required()
def get_contact(id):
    contact = Contact.query.filter_by(id=id).first()

    print('contact-outlets are: ', contact.outlets[0])

    if not contact:

        return jsonify({'message':'Item not found'}), HTTP_404_NOT_FOUND

    return jsonify({
        'id':contact.id,
        'name':contact.name,
        'email':contact.email,
        'bio':contact.bio,
        'image_url':contact.image_url,
        'outlets':contact.outlets,
        'created_at':contact.created_at,
        'updated_at':contact.updated_at,
    }), HTTP_200_OK

Here is the relevant portion of the database schema if it helps:

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import relationship

db = SQLAlchemy()

class Contact(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    bio = db.Column(db.Text, nullable=True)
    image_url = db.Column(db.Text)
    visits = db.Column(db.Integer, default=0)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    created_at = db.Column(db.DateTime, default=datetime.now())
    updated_at = db.Column(db.DateTime, onupdate=datetime.now())
    calls = db.relationship("Call", backref="call_contact", lazy="joined")
    outlets = db.relationship("Outlet", secondary="contact_outlet", backref="contact", lazy="joined")

    def __repr__(self) -> str:
        return 'Contact>>> {self.name}'

class Outlet(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True, nullable=False)
    website = db.Column(db.Text)
    description = db.Column(db.Text)
    image_url = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.now())
    updated_at = db.Column(db.DateTime, onupdate=datetime.now())
    calls = db.relationship("Call", backref="outlet", lazy="joined")
    contacts = db.relationship("Contact", secondary="contact_outlet", lazy="joined")

    def __repr__(self) -> str:
        return 'Outlet>>> {self.name}'

    def to_json(self):
        return {
            "id": self.id,
            "name": self.name,
            "website": self.website,
            "description": self.description,
            "image_url": self.image_url,
            "contacts": self.contacts
        }

contact_outlet = db.Table('contact_outlet',
    db.Column('contactId', db.Integer, db.ForeignKey("contact.id"), primary_key=True),
    db.Column('outletId', db.Integer, db.ForeignKey("outlet.id"), primary_key=True)
)

The error is direct, that you have values that are not JSON ready in your response.

You have an item in your dict, 'outlets':contact.outlets, , which is ideally a list of Outlet objects as per your model definition. You need to tell flask-jsonify , how to make that object a JSON

In a normal json.dumps operation, you can do this by passing a custom encoder or default method. You can do this, by setting the encoder option in flask.

First, you create a custom model encoder for JSON. An example could be like below

from flask import json
class ModelEncoder(json.JSONEncoder):
    def default(self, o: Any) -> Any:
        if hasattr(o, 'to_json'):
            return o.to_json()
        else:
            return super(ModelEncoder, self).default(o)

And now, ask flask to use this encoder for converting models to JSON. For this you can set the flask app configuration.

app.json_encoder = ModelEncoder

If you have a to_json method in your model, it will call that for serializing those. Other wise it follows the default. Yes, this is a simple implementation, you can improvise with type checks or variations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM