1

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)
)
2
  • What is class Outlet? Commented Feb 27, 2022 at 13:21
  • @buran added the db schema to the question above. Commented Feb 27, 2022 at 13:35

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Do I do this all in my init.py within my create_app?
getting NameError: name 'Any' is not defined
Any is a type hint. You can import it from typing or remove it.
Cool. You can upvote as well, if its a good solution
Done. @Kris do you do tutoring? I probably need an hour just to get these relations working correctly. Is there a way we can connect?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.