Reputation: 1127
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)
)
Upvotes: 1
Views: 3462
Reputation: 8868
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.
Upvotes: 3