Trying to implement a rather simple REST API in Python (SQLAlchemy + Marshmallow) using HATEOAS resource linking, I am stuck when attempting to create "smart hyperlinks" for one-to-many relationships.
Let's consider the classic book example: One book has exactly one publisher, but 1 to n authors. To keep it simple, in my use case it would be sufficient if every author could just write one book. My class structure could then look like this:
models/book.py
:
class Book(db.Model):
__tablename__ = 'book'
book_id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255))
publisher_id = db.Column('publisher_id', db.Integer, db.ForeignKey('publisher.publisher_id'))
authors = db.relationship('Author', back_populates='book_id')
# some more methods ...
@staticmethod
def get_by_id(book_id):
return Book.query.get(book_id)
models/author.py
:
class Author(db.Model):
__tablename__ = 'author'
author_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
book_id = db.Column('book_id', db.Integer, db.ForeignKey('book.book_id'))
booking = db.relationship('Book', back_populates='authors')
# some more methods ..
@staticmethod
def get_by_id(author_id):
return Authors.query.get(author_id)
models/book_schema.py
:
class BookSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Book
book_id=fields.Integer()
title = fields.String()
# Smart hyperlinking
_links = ma.Hyperlinks(
{
"self": {"href": ma.AbsoluteURLFor("book", book_id="<booking_id>")},
"publisher": {"href": ma.AbsoluteURLFor("publishers", publisher_id="<publisher_id>")},
"authors": {"href": ma.AbsoluteURLFor("authors", author_id="<authors>")} #!!!
}
)
app/__init__.py
:
def create_app(config_name):
#...
api.add_resource(Book, '/books/<int:book_id>', endpoint='books')
#...
The line in book_schema.py
denoted with #!!!
is the one causing trouble. I do understand, that authors
is a collection and therefore there is no single author_id
. But I haven't found any way to add this collection to the hyperlinks.
What I would like to get when querying a book at, let's say, /books/123
looks like this:
{
"book_id": 123,
"title": "How to think of a good book title"
"_links": {
"publisher": {
"href": "https://localhost:5000/publishers/4711"
},
"authors": [
{ "href": "https://localhost:5000/authors/42" },
{ "href": "https://localhost:5000/authors/333" },
{ "href": "https://localhost:5000/authors/1337" }
]
}
}
Concerning everything else but the one-to-many relation, it works very well. The publisher relation is also populated as expected. I can also verify, that the authors of a book are loading correctly from the database (by debugging in get_by_id
of the Book
class).
Additionally, I have found this GitHub issue, which, if I got it right, addresses my problem as well.
So, finally, my question is: Is there any way I can achieve the collection serialization? If not by standard means of Marshmallow (I didn't find anything there), then perhaps by any sane kind of post-processing the response before it is sent to the client?
Thanks a lot in advance!