4

Problem

I am building an app on Flask, Flask-SQLAlchemy, and Flask-Restless. I have used restless to generate an API for a parent-child-grandchild relationship*. A GET on my child will correctly fetch the grandchild, but a GET on the parent will not fetch the grandchild for each child.

*In fact, the parent-child relationship is a many-to-many, but same premise.

Models

class Grandchild(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)

    parent = db.relationship('Child', backref='grandchild')

parent_child = db.Table('parent_child', 
    db.Column('parent_id', db.Integer, db.ForeignKey('parent.id')),
    db.Column('child_id', db.Integer, db.ForeignKey('child.id')),
    db.Column('number', db.SmallInteger)
)

class Child(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)

    grandchild_id = db.Column(db.Integer, db.ForeignKey('grandchild.id'), nullable=False)

class Parent(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)

    children = db.relationship('Child', secondary=parent_child)

api.create_api(Child, exclude_columns=['grandchild_id'])
api.create_api(Parent)

GET: /api/child response

{
  "num_results": 1, 
  "objects": [
    {
      "id": 1, 
      "name": "test"
      "grandchild": {
        "id": 1, 
        "name": "test"
      }
    }
  ], 
  "page": 1, 
  "total_pages": 1
}

GET: /api/parent response

{
  "num_results": 1, 
  "objects": [
    {
      "children": [
        {
          "id": 1, 
          "name": "test", 
          "grandchild_id": 1
        }
      ],
      "id": 1, 
      "name": "test"
    }], 
  "page": 1, 
  "total_pages": 1
}
Matthew James Davis
  • 12,134
  • 7
  • 61
  • 90

2 Answers2

9

postprocessors can be used to fetch grandchild.

def parent_post_get_many(result=None, search_params=None, **kw):
   for object in result['objects']:
      for child in object['children']:
         grandchild = Grandchild.query.get(child['grand_child_id'])
         child.update({'grandchild': grandchild})

api.create_api(Parent, postprocessors={'GET_MANY': [parent_post_get_many]})
ramavarsh
  • 166
  • 3
  • 4
  • This seems to include an instance of the model in the data structure, rather than a serialized instance, which results in an error from the JSON serializer. Looks at restless's internals, it seems the serialization function is "serializer()" on the API instance. Do you know if it's possible to access that to serialize the instance? – Andrew Hows Dec 20 '16 at 23:50
2

After looking at this for a few hours I'm going to give the best answer I have at the moment. I've tried a number of approaches and haven't been able to get anything to successfully render the grandchild, so I turned to the flask-restless issues tracker to see what I could find:

https://github.com/jfinkels/flask-restless/pull/222#issuecomment-31326359 and @jfinkels response seem to indicate that what you want is currently not possible in flask-restless.

Assuming my assessment of the state of issues is correct, you may want to look into either designing around this issue, or using a different package to serve your API (perhaps Flask-restful, though I admit I haven't used it and don't know if it's suitable).

FWIW, I've been using Flask-Classy to build a json API for a project I'm working on. The process is a little more involved than I suspect you want, but it also comes with enough freedom to better control what queries are used and how the results are serialized.

abathur
  • 1,047
  • 7
  • 19
  • 1
    I came to the same conclusion you did, and begun using Flask-Restful. It has its own challenges, but it seems to give a lot more control than Flask-Restless, so I've been moving forward with it. I don't fully understand the problem Flask-Classy solves. It seems like it is a fairly extensible tool for building an extremely basic REST api. There's no mention of queries, ORMs, anything. – Matthew James Davis Apr 09 '14 at 05:56
  • 1
    Correct--it would take a lot more work to get what I think you need out of Flask-Classy. I was just alerting you to its existence in case you exhaust the options for getting what you need out of packages geared towards exposing a REST api with minimal intervention. – abathur Apr 09 '14 at 14:13