-1

I am working on a simple CRUD application and I can't return an array of objects in Flask.

My db Model is like this:

class QrRecord(db.Model):
  id = db.Column(db.String(256), primary_key=True)
  name = db.Column(db.String(128), nullable=False)
  date = db.Column(db.Date, nullable=False)
  qrType = db.Column(db.String(128), nullable=False)
  count = db.Column(db.Integer, nullable=False)

  def toJson(self):
    return jsonify(
      {
        "id": self.id,
        "name": self.name,
        "date": self.date,
        "qrType": self.qrType,
        "count": self.count
      }
    )

And I am trying to return it like so:

@app.route("/qr", methods=["GET"])
def qr_get():
  tasks: list = QrRecord.query.order_by(QrRecord.date).all()
  return jsonify(tasks)

The error I get is:

<title>TypeError: Object of type type is not JSON serializable
  // Werkzeug Debugger</title>

When I am returning one single object as return return tasks[0].toJson(), it works just fine. The same when I would return an array of anything else, just like return jsonify([1, 2, 3]).

Would anybody know how to return the array of QrRecords? I feel like I have tried every solution already.

Daniel Nýdrle
  • 153
  • 1
  • 9
  • `return jsonify(tasks)` – python_user May 10 '23 at 14:44
  • @python_user That was a typo that I fixed, but I am still getting the same response, but with "Object of type Response" instead of "Object of type type". I also tried it as `return jsonify([task.toJson() for task in tasks]), 200`, but still the same. – Daniel Nýdrle May 10 '23 at 14:50
  • What's `toJson`? How would `jsonify` know to call it? – ggorlen May 10 '23 at 16:19
  • @ggorlen That is a method I defined in the QrRecord class. – Daniel Nýdrle May 10 '23 at 17:17
  • I can see that, but based on what documentation did you pick the name? How would `jsonify` know to call it? Just because you happen to have the word "json" in it doesn't mean `jsonify` knows anything about it. You could have called the method `asdashdd_foo` and you'd have the same result, no? – ggorlen May 10 '23 at 17:38
  • @ggorlen Well, that's the thing. If I were to write `return jsonify({"id": tasks[0], ...})` it would work, but I do not want to loop through all the items like that (https://pastebin.com/jdRvquQQ - this is working). `toJson()` is just a name I came up for a method that I used in the snippet so that I could show that for one item, but not for more items in a list. – Daniel Nýdrle May 10 '23 at 19:19
  • 1
    Yes, `jsonify` knows what to do with nested primitive structures (lists and dicts) but not with your model class. I'd make it an `@dataclass` (see [How to serialize SqlAlchemy result to JSON?](https://stackoverflow.com/a/57732785/6243352)), or use `jsonify([x.toJson() for x in tasks])` and remove `jsonify()` from the `toJson` method so it just returns a dict. – ggorlen May 10 '23 at 19:41
  • 1
    I think you can change the `toJson()` function to just return the dict (without calling `jsonify()`) and then change the `qr_get()` function to `return jsonify([task.toJson() for task in tasks]), 200`. Let me know if this helps! – DasaniT May 11 '23 at 00:33
  • Both of your solutions worked for me, thank you very much for the help! – Daniel Nýdrle May 11 '23 at 08:19

2 Answers2

0

This answer has already been more or less answered to here: How to serialize SqlAlchemy result to JSON?

Using @dataclass together with statically typed params helped:

@dataclass
class QrRecord(db.Model):
  id: str = db.Column(db.String(256), primary_key=True)
  name: str = db.Column(db.String(128), nullable=False)
  date: datetime = db.Column(db.Date, nullable=False)
  qrType: str = db.Column(db.String(128), nullable=False)
  count: int = db.Column(db.Integer, nullable=False)

@app.route("/qr", methods=["GET"])
def qr_get():
  tasks: list = QrRecord.query.order_by(QrRecord.date).all()
  return jsonify(tasks)
Daniel Nýdrle
  • 153
  • 1
  • 9
0

The issue is that tasks is a list of db objects, which (as the error says) is not JSON serializable. You can solve this by iterating over each object in your query, getting it's toJson() return value, then adding it to the list. Then, you should be able to simply return the list.

@app.route("/qr", methods=["GET"])
def qr_get():
  tasks = []
  for record in QrRecord.query.order_by(QrRecord.date).all():
    tasks.append(record.toJson())
  return tasks
Rob
  • 518
  • 1
  • 3
  • 18