Search code examples
pythonrestflaskflask-sqlalchemyflask-marshmallow

accesing model props in flask-marshmallow schema class with SQLAlchemyAutoSchema


I have a simple db model which references itself, to create a hierarchical structure. Now I want to build a RESTapi, using Flask, Flask-SQLAlchemy and Flask-marshmallow. For convenience reasons I inheriet my Schema-classes from ma.SQLAlchemyAutoSchema (where ma is the instance of flask-marshmallow)

If the model has children, I want my endpoint to return the category object, including a link to the endpoint that returns the children. (which works fine.) But if the model has no children, the link shall not be included.

My gut feeling tells me that you could and and probably should set this directly in the schema definition.

#model.py
from app import db

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    parent_id = db.Column(db.Integer, db.ForeignKey("category.id", ondelete="SET NULL"))
    children = db.relationship("Category",
                                     backref=db.backref('parent', remote_side=[id]),
                                     lazy="dynamic")

#schema.py
from app import model, ma, db

class CategorySchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = model.Category

    _links = ma.Hyperlinks({
        'children': ma.URLFor('api.category_children', equipment_id="<id>")
        if model.Category.query.filter_by(id=??????).first().children.first() else None 
    })

I came up with the simple ternary assignment above, but I can not figure out, where to get the id (id=??????) from, (which is the very same id that ma.URLFor() accesses via equipment_id="<id>").

expected example output, if children exist

  {
    "_links": {
      "children": "/api/v1/category/1/children"

    }, 
    "id": 1, 
    "name": "some_name"
  }

expected example output, if no children exist:

  {
    "_links": {
      "children": null 

    }, 
    "id": 1, 
    "name": "some_name"
  }

thank you in advance


Solution

  • I think you're over engineering your design. Just having:

    class CategorySchema(ma.SQLAlchemyAutoSchema):
        class Meta:
            model = model.Category
    
        _links = ma.Hyperlinks({
            'children': ma.URLFor('api.category_children', equipment_id="<id>")
        })
    

    should be fine. If the Parent has no Children, then hitting the endpoint (in your example) of "/api/v1/category/1/children" should just return an empty result set. You shouldn't need to control the navigation links they way you've described. The expectation from HATEOAS design is you should be able to navigate consistently through the resources to the desired endpoint. Having to deal with a null value for a link rather than an empty result, IMO is setting yourself for a headache down the line.

    Otherwise, if you really want/need to. The post_dump decorator should give you access to find the id value.