0
Package Version
Python 3.9.7
Flask 2.0.2
Flask-UUID 0.2
pip 21.3.1
setuptools 57.4.0
Werkzeug 2.0.1

Hi there, although being familiar with other similar concepts in other languages, this leaves me quite stunned .. .. I am writing a little petstore to get familiar with python - more specific flask - and stumble from error to error. The 'normal way to learn I guess ^.^

Nevermind, the code is primarily from here (https://programminghistorian.org/en/lessons/creating-apis-with-python-and-flask#creating-the-api) and works fine ..

import flask
from flask import request, jsonify

app = flask.Flask(__name__)
app.config["DEBUG"] = True

# Create some test data for our catalog in the form of a list of dictionaries.
books = [
    {'id': 0,
     'title': 'A Fire Upon the Deep'},
    {'id': 1,
     'title': 'The Ones Who Walk Away From Omelas'},
    {'id': 2,
     'title': 'Dhalgren'}
]

@app.route('/', methods=['GET'])
def home():
    return '''<h1>Distant Reading Archive</h1>
<p>A prototype API for distant reading of science fiction novels.</p>'''

# A route to return all of the available entries in our catalog.
@app.route('/api/v1/resources/books/all', methods=['GET'])
def api_all():
    return jsonify(books)

app.run()

.. but .. instead of a prewritten array I'd like to paste some objects before switching to an sqlite-persisence-solution (or likewise).

As far as possible I want to raise the complexity slowly to grasp the program completely.

Hence, using a customized pet class:

import flask
from flask import request, jsonify
import uuid

app = flask.Flask(__name__)
app.config["DEBUG"] = True

class Pet:
    def __init__(self,id, name, dateOfBirth):
      self.id = id.hex
      self.name = name
      self.dateOfBirth = dateOfBirth
#    def to_json(obj):
#        return json.dumps(obj, default=lambda obj: obj.__dict__)

# Create some test data for our catalog in the form of a list of dictionaries.
pets = [ Pet(uuid.uuid4(),'Hamster', '02.12.2019' )]
pets.append( Pet(uuid.uuid4(),'Fish', '07.03.1985' ))
pets.append( Pet(uuid.uuid4(),'Dog', '26.11.2000' ))
pets.append( Pet(uuid.uuid4(),'Cat', '17.05.2021' ))

@app.route('/', methods=['GET'])
def home():
    return "<h1>Petshop Archive</h1><p>This site is a prototype API for the petshop archive.</p>"

# A route to return all of the available entries in our catalog.
@app.route('/api/v1/resources/pets/all', methods=['GET'])
def api_all():
    return jsonify(pets)

if __name__ == '__main__':
    app.run(host='0.0.0.0')

.. You see, the changes are marginal. Now, requesting the API ( http://localhost:5000/api/v1/resources/pets/all ) fails giving a "TypeError: Object of type Pet is not JSON serializable" from the line ..

return jsonify(pets)

.. fair enough. Of course I googled and found fitting solutions, such as ..

  1. How to make a class JSON serializable

.. and ..

  1. python json object array not serializable

.. for the serializable problem (generally) and added the appropriate function

def to_json(obj):
        return json.dumps(obj, default=lambda obj: obj.__dict__)

Still I missed the link between the function (serializing the pet-class) and the array (recognizing the pet class to be serializabe).

My approach to write a new array to prevent any class having to be serialized by returning 'pets[pet]' ..

def api_all():
    allpets = [pet.toJson for pet in pets]
    return jsonify(allpets)

.. fails as well.

Hence the -> TypeError: Object of type Pet is not JSON serializable <- still exists.

Is there 'fancy way' to solve this issue? I can't imagine that this issue has not been raised, yet. Maybe I just looked at it from a wrong angle ;-)

Thanks everyone who made it this far :)

1 Answer 1

1

Finally, this helped: --> How to make a class JSON serializable

It works if I am using an encoder in combination with a loop over the array.

from json import JSONEncoder

class PetEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__  

# ... ... ...

# A route to return all of the available entries in our catalog.
@app.route('/api/v1/resources/pets/all', methods=['GET'])
def api_all():
    allpets = [PetEncoder().encode(pet) for pet in pets]
    return jsonify(allpets)

Still it feels dirty, as I need to rewrite the whole array 'unneccessarily'. I bet there has to be a better solution. Nevertheless, an array is obviously not the desired 'longterm-persisence-solution'-datatype.


Last, the whole code so far:

import flask
from flask import request, jsonify
import uuid
from json import JSONEncoder

app = flask.Flask(__name__)
app.config["DEBUG"] = True

class PetEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__   

class Pet:
    def __init__(self,id, name, dateOfBirth):
      self.id = id.hex
      self.name = name
      self.dateOfBirth = dateOfBirth

# Create some test data for our catalog in the form of a list of dictionaries.
pets = [ Pet(uuid.uuid4(),'Hamster', '02.12.2019' )]
pets.append( Pet(uuid.uuid4(),'Fish', '07.03.1985' ))
pets.append( Pet(uuid.uuid4(),'Dog', '26.11.2000' ))
pets.append( Pet(uuid.uuid4(),'Cat', '17.05.2021' ))

@app.route('/', methods=['GET'])
def home():
    return "<h1>Petshop Archive</h1><p>This site is a prototype API for the petshop archive.</p>"

# A route to return all of the available entries in our catalog.
@app.route('/api/v1/resources/pets/all', methods=['GET'])
def api_all():
    allpets = [PetEncoder().encode(pet) for pet in pets]
    return jsonify(allpets)

if __name__ == '__main__':
    app.run(host='0.0.0.0')
Sign up to request clarification or add additional context in comments.

Comments

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.