User Guide

Introduction

Flask-RESTful-DBBase creates resources with a helpful set of defaults, but provides numerous fallback positions to customize the resource capabilities to the requirements of the problem.

The capabilities are presented with a few simple apps designed to highlight the features.

A First Look

We will create a small app that shows some of the capabilities.

First, we import and configure the database. Within the Flask environment, DBBase provides a thin wrapper around Flask-SQLAlchemy but with a different Model class.

The data-aware resources are then imported. Once that is done, a couple database models are created that we will use for our example.

Flask-RESTful Resource subclasses implement this functionality.

  • ModelResource – This class implements the standard methods associated with interacting with a single record, namely GET, POST, PUT, PATCH, and DELETE.

  • CollectionModelResource – This class implements a GET for collections. The other methods, being less common for collections, have been left off to avoid activating an unwanted (surprise) capability.

  • MetaResource – This class implements a GET that documents the capabilities of a resource class.

The code for this example is found in the examples section as

Initialize the App and Models

from flask import Flask
from flask_restful import Api
from flask_restful_dbbase import DBBase
from flask_restful_dbbase.resources import (
    CollectionModelResource,
    ModelResource,
    MetaResource,
)

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

api = Api(app)
db = DBBase(app)


class Author(db.Model):
    __tablename__ = "author"

    id = db.Column(db.Integer, primary_key=True, nullable=True)
    first_name = db.Column(db.String(50), nullable=False)
    last_name = db.Column(db.String(50), nullable=False)

    def full_name(self):
        return f"{self.first_name} {self.last_name}"

    books = db.relationship("Book", backref="author", lazy="joined")


class Book(db.Model):
    __tablename__ = "book"

    id = db.Column(db.Integer, primary_key=True, nullable=True)
    isbn = db.Column(db.String(20), nullable=True)
    title = db.Column(db.String(100), nullable=False)
    pub_year = db.Column(db.Integer, nullable=False)
    author_id = db.Column(
        db.Integer, db.ForeignKey("author.id"), nullable=False
    )


db.create_all()

Next, some sample records are created so that we have something to look at.

author = Author(first_name="Geoffrey", last_name="Cooper").save()

book = Book(
    isbn="0-87893-214-3",
    title="The Cell: A Molecular Approach, 3rd edition",
    pub_year=2004,
    author_id=author.id,
).save()
book = Book(
    isbn="978-0878932191",
    title="The Cell: A Molecular Approach, 4th edition",
    pub_year=2006,
    author_id=author.id,
).save()

book = Book(
    isbn="978-1605352909",
    title="The Cell: A Molecular Approach, 7th edition",
    pub_year=2015,
    author_id=author.id,
).save()

book = Book(
    isbn="978-1605357072",
    title="The Cell: A Molecular Approach, 9th edition",
    pub_year=2018,
    author_id=author.id,
).save()

author = Author(first_name="Serope", last_name="Kalpakjian").save()

book = Book(
    isbn="978-0133128741",
    title="Manufacturing Engineering & Technology (7th Edition)",
    pub_year=2013,
    author_id=author.id,
).save()

author = Author(first_name="Steven", last_name="Skiena").save()

Create Resources

Now we can create some resources that will use these models.

class BookCollection(CollectionModelResource):
    model_class = Book


class BookResource(ModelResource):
    model_class = Book


class AuthorCollection(CollectionModelResource):
    model_class = Author


class AuthorResource(ModelResource):
    model_class = Author

So what will this do? Once added to the API, the methods GET, POST, PUT, PATCH, and DELETE will be available at the default URLs of:

/books              <-- the collection resource URL
/books              <-- the URL for post
/books/<int:id>     <-- the URL for get/put/patch/delete

/authors            <-- the collection resource URL
/authors            <-- the URL for post
/authors/<int:id>   <-- the URL for get/put/patch/delete

Another resource type, the MetaResource, can provide detailed information about your API and the requirements and capabilities by URL.

Such meta resources are created by informing the meta resource of the resource class.

class BookMetaCollection(MetaResource):
    resource_class = BookCollection


class BookMeta(MetaResource):
    resource_class = BookResource


class AuthorMetaCollection(MetaResource):
    resource_class = AuthorCollection


class AuthorMeta(MetaResource):
    resource_class = AuthorResource

Once added to the API, The following URLs will become available.

/meta/authors/single
/meta/authors/collection
/meta/books/single
/meta/books/collection

Finally, the resources are added to the api. You can see below that the URLs are generated by the resources themselves. These functions are for convenience, the URLs can also be added in the usual way with Flask-RESTful as well.

api.add_resource(AuthorCollection, *AuthorCollection.get_urls())
api.add_resource(AuthorResource, *AuthorResource.get_urls())
api.add_resource(AuthorMetaCollection, *AuthorMetaCollection.get_urls())
api.add_resource(AuthorMeta, *AuthorMeta.get_urls())

api.add_resource(BookCollection, *BookCollection.get_urls())
api.add_resource(BookResource, *BookResource.get_urls())
api.add_resource(BookMetaCollection, *BookMetaCollection.get_urls())
api.add_resource(BookMeta, *BookMeta.get_urls())


if __name__ == "__main__":
    app.run(debug=True, port=5000)

Use the API

As a first step, let us get a book.

# get a book
curl http://localhost:5000/books/1 \
    -H "Content-Type: application/json"
{
    "author": {
        "firstName": "Geoffrey",
        "lastName": "Cooper",
        "fullName": "Geoffrey Cooper",
        "id": 1
    },
    "title": "The Cell: A Molecular Approach, 3rd edition",
    "authorId": 1,
    "id": 1,
    "isbn": "0-87893-214-3",
    "pubYear": 2004
}

Note that the column names have been converted to camel case. Also, because a relationship has been specified in the table, the author has been included by default. As to what shows or does not, that is under your control through configuration.

Now we will post some data. But first, let us look at error checking with invalid data.

# post a book, but with invalid data
curl http://localhost:5000/books \
    -H "Content-Type: application/json" \
    -d '{"authorId": 1,
         "title": "this is a test woah, this is really a long title, woah, this is really a long title, woah, this is really a long title, woah, this is really a long title, woah, this is really a long title "}'
{
    "message": [
        {
            "title": "The data exceeds the maximum length 100"
        },
        {
            "missing_columns": [
                "pub_year"
            ]
        }
    ]
}

Here we get back an error message. Data validation compares the data received versus what can posted to the database. An attempt is made to provide meaningful errors so that an appropriate action can be taken. In this case the publication year is missing and the title is too long.

Now we can post a book that does not have errors:

# post a book with valid data
curl http://localhost:5000/books \
    -H "Content-Type: application/json" \
    -d '{"authorId": 3,
         "title": "The Algorithm Design Manual",
         "pubYear": 1997,
         "isbn": "0-387-94860-0"}'
{
    "author": {
        "firstName": "Steven",
        "lastName": "Skiena",
        "fullName": "Steven Skiena",
        "id": 3
    },
    "title": "The Algorithm Design Manual",
    "authorId": 3,
    "id": 6,
    "isbn": "0-387-94860-0",
    "pubYear": 1997
}

We can select from our collection of books:

# get all books
curl http://localhost:5000/books \
    -H "Content-Type: application/json"
{
    "Book": [
        {
            "author": {
                "firstName": "Geoffrey",
                "lastName": "Cooper",
                "fullName": "Geoffrey Cooper",
                "id": 1
            },
            "title": "The Cell: A Molecular Approach, 3rd edition",
            "authorId": 1,
            "id": 1,
            "isbn": "0-87893-214-3",
            "pubYear": 2004
        },
        {
            "author": {
                "firstName": "Geoffrey",
                "lastName": "Cooper",
                "fullName": "Geoffrey Cooper",
                "id": 1
            },
            "title": "The Cell: A Molecular Approach, 4th edition",
            "authorId": 1,
            "id": 2,
            "isbn": "978-0878932191",
            "pubYear": 2006
        },
        {
            "author": {
                "firstName": "Geoffrey",
                "lastName": "Cooper",
                "fullName": "Geoffrey Cooper",
                "id": 1
            },
            "title": "The Cell: A Molecular Approach, 7th edition",
            "authorId": 1,
            "id": 3,
            "isbn": "978-1605352909",
            "pubYear": 2015
        },
        {
            "author": {
                "firstName": "Geoffrey",
                "lastName": "Cooper",
                "fullName": "Geoffrey Cooper",
                "id": 1
            },
            "title": "The Cell: A Molecular Approach, 9th edition",
            "authorId": 1,
            "id": 4,
            "isbn": "978-1605357072",
            "pubYear": 2018
        },
        {
            "author": {
                "firstName": "Serope",
                "lastName": "Kalpakjian",
                "fullName": "Serope Kalpakjian",
                "id": 2
            },
            "title": "Manufacturing Engineering & Technology (7th Edition)",
            "authorId": 2,
            "id": 5,
            "isbn": "978-0133128741",
            "pubYear": 2013
        },
        {
            "author": {
                "firstName": "Steven",
                "lastName": "Skiena",
                "fullName": "Steven Skiena",
                "id": 3
            },
            "title": "The Algorithm Design Manual",
            "authorId": 3,
            "id": 6,
            "isbn": "0-387-94860-0",
            "pubYear": 1997
        }
    ]
}

Meta Information

Finally, we can document our API using meta information. This enables comprehensive information to be given to users of your API.

The following output details the entire book resource. Incidently, we could have also specified curl -g http://localhost:5000/meta/books/single?get to see only the GET method requirements.

# get documentation
curl -g http://localhost:5000/meta/books/single
{
    "modelClass": "Book",
    "urlPrefix": "/",
    "baseUrl": "/books",
    "methods": {
        "get": {
            "url": "/books/<int:id>",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "author": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Author",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "firstName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "lastName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "fullName": {
                                        "readOnly": true
                                    }
                                }
                            }
                        },
                        "title": {
                            "type": "string",
                            "maxLength": 100,
                            "nullable": false,
                            "info": {}
                        },
                        "authorId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "author.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "isbn": {
                            "type": "string",
                            "maxLength": 20,
                            "nullable": true,
                            "info": {}
                        },
                        "pubYear": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "post": {
            "url": "/books",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "isbn": {
                    "type": "string",
                    "maxLength": 20,
                    "nullable": true,
                    "info": {}
                },
                "title": {
                    "type": "string",
                    "maxLength": 100,
                    "nullable": false,
                    "info": {}
                },
                "pubYear": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "authorId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "author.id",
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "author": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Author",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "firstName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "lastName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "fullName": {
                                        "readOnly": true
                                    }
                                }
                            }
                        },
                        "title": {
                            "type": "string",
                            "maxLength": 100,
                            "nullable": false,
                            "info": {}
                        },
                        "authorId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "author.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "isbn": {
                            "type": "string",
                            "maxLength": 20,
                            "nullable": true,
                            "info": {}
                        },
                        "pubYear": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "put": {
            "url": "/books/<int:id>",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "isbn": {
                    "type": "string",
                    "maxLength": 20,
                    "nullable": true,
                    "info": {}
                },
                "title": {
                    "type": "string",
                    "maxLength": 100,
                    "nullable": false,
                    "info": {}
                },
                "pubYear": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "authorId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "author.id",
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "author": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Author",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "firstName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "lastName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "fullName": {
                                        "readOnly": true
                                    }
                                }
                            }
                        },
                        "title": {
                            "type": "string",
                            "maxLength": 100,
                            "nullable": false,
                            "info": {}
                        },
                        "authorId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "author.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "isbn": {
                            "type": "string",
                            "maxLength": 20,
                            "nullable": true,
                            "info": {}
                        },
                        "pubYear": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "patch": {
            "url": "/books/<int:id>",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "isbn": {
                    "type": "string",
                    "maxLength": 20,
                    "nullable": true,
                    "info": {}
                },
                "title": {
                    "type": "string",
                    "maxLength": 100,
                    "nullable": false,
                    "info": {}
                },
                "pubYear": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "authorId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "author.id",
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "author": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Author",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "firstName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "lastName": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "fullName": {
                                        "readOnly": true
                                    }
                                }
                            }
                        },
                        "title": {
                            "type": "string",
                            "maxLength": 100,
                            "nullable": false,
                            "info": {}
                        },
                        "authorId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "author.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "isbn": {
                            "type": "string",
                            "maxLength": 20,
                            "nullable": true,
                            "info": {}
                        },
                        "pubYear": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "delete": {
            "url": "/books/<int:id>",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {}
            ]
        }
    },
    "table": {
        "Book": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "isbn": {
                    "type": "string",
                    "maxLength": 20,
                    "nullable": true,
                    "info": {}
                },
                "title": {
                    "type": "string",
                    "maxLength": 100,
                    "nullable": false,
                    "info": {}
                },
                "pub_year": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "author_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "author.id",
                    "info": {}
                },
                "author": {
                    "readOnly": true,
                    "relationship": {
                        "type": "single",
                        "entity": "Author",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "first_name": {
                                "type": "string",
                                "maxLength": 50,
                                "nullable": false,
                                "info": {}
                            },
                            "last_name": {
                                "type": "string",
                                "maxLength": 50,
                                "nullable": false,
                                "info": {}
                            },
                            "full_name": {
                                "readOnly": true
                            }
                        }
                    }
                }
            },
            "xml": "Book"
        }
    }
}

With the use of method decorators for authenticated users, a fair number of database tables can be presented via an API with just this vanilla approach.

In the next section we will look at a few tweaks and modifications that can handle more complicated issues.

Next: Simple Owner App

Simple Owner App

We discussed how resources use the table models as the basis for validating incoming data, and presentation of return data. Now we will look at how additional capabilities can be be brought to bear.

To get a better understanding of why these capabilities matter, let us look at a few scenarios and see how they can be solved by taking some additional actions.

We touched on how access to data can be limited to authenticated users by table using method decorators, a feature that is part of Flask-RESTful. But suppose we want to let users only see information related to their own accounts? That would mean that we need to restrict on a record by record basis, not the table or decorator method.

This would be time to write some code for that method that gets the authenticated user_id, does some checking and passes it along, or not. But then, you would need to write the rest of the code for that method.

We will still write some code, but by inserting it in the right place there should be less of it.

The ModelResource class by design contains places to alter the flow. For example, the following modifications can be made to the POST process.

  • Can append an additional filter to a query, or replace it entirely.

  • After validation, a data record is created, an additional modification can be made just prior to a commit.

  • After a commit, actions can be taken.

The last two options can also be used to submit jobs to queues and return a job object instead, whatever else fits the situation.

Owner Based Tables

This description has been somewhat abstract, so we will now create a simplified example to show possible modifications to the process in a concrete way.

We will control

  • who sees what

  • altering the flow of processing

  • changing the response returned to the user

We will subclass the ModelResource and CollectionResource and add a few entries to limit access to the right parties. With new subclassed resources we will have a basis for creating a set of resources that enforce privacy of the users information.

Our result will be clean code, such as the following;

class OrderResource(OwnerResource):
    model_class = Order

class OrderCollection(OwnerCollectionResource):
    model_class = Order

As before, the code for this example is found in the examples section as

Initiate the App and Models

To implement this example, we will create two tables:

  • A user table

  • An order table, that is apparently a request for a service.

As before, we initialize the app:

from functools import wraps
from datetime import date, datetime
from flask import Flask, request
from flask_restful import Api
from flask_restful_dbbase import DBBase
from flask_restful_dbbase.resources import (
    CollectionModelResource,
    ModelResource,
    MetaResource,
)

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

api = Api(app)
db = DBBase(app)

First, we create the User model. Of course, password would normally be encrypted, but note the use of a column class of WriteOnlyColumn. This means that when returning results, the password would be automatically excluded, which can limit awkward mistakes, even when appropriately encrypted.

class User(db.Model):
    __tablename__ = "user"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    username = db.Column(db.String(80), nullable=False)
    password = db.WriteOnlyColumn(db.String(80), nullable=False)
    email = db.Column(db.String(80), unique=True, nullable=False)

    is_staff = db.Column(db.Boolean, default=False, nullable=False)
    is_active = db.Column(db.Boolean, default=False, nullable=False)
    is_account_current = db.Column(db.Boolean, default=False, nullable=False)

    date_joined = db.Column(db.Date, default=date.today, nullable=False)
    last_login = db.Column(db.DateTime, nullable=True)

Next, we create an Order model. Think of this as some useful service that is requested by the user.

class Order(db.Model):
    __tablename__ = "order"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    owner_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
    description = db.Column(db.String, nullable=False)
    ordered_at = db.Column(db.DateTime, default=datetime.now)
    status_id = db.Column(db.SmallInteger, default=0, nullable=True)

Now we create the database and create a couple users.

db.create_all()

# create users
user = User(
    username="our_main_user",
    password="verysecret",
    email="user_mainexample.com",
).save()

user = User(
    username="another_user",
    password="also_quite_secret",
    email="another@example.com",
).save()

The point of this example is to show how to control restricted services and make modifications. Accordingly, we will gloss over the portion of the API for the user register/confirm/log-in process by simply creating users and focus on the creation of orders.

def mock_jwt_required(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        user = request.headers.get("Authorization", None)
        if user is not None and user.startswith("User"):
            # we're completely secure, sir
            return fn(*args, **kwargs)
        return {"message": "Unauthorized User"}, 401

    return wrapper


def get_identity():
    user = request.headers.get("Authorization", None)
    user_id = int(user.split(":")[1])
    return user_id

Create the Owner-Based Resources

Since control is to be record-based, not table-based, we will need to know

  • who is the user?

  • permit the user to see only what records this user is permitted

To that end, we will create a subclass of ModelResource and CollectionModelResource that will take advantage of Flask-RESTful method decorators and combine it with a minor modification to a ModelResource and CollectionModelResource. That way we can code down on total code.

An OwnerResource and OwnerCollectionResource will created.

Mock Authentication

We will create a mock authentication method decorator that is loosely based on Flask-JWT-Extended.

class OwnerResource(ModelResource):
    """
    Pretend that jwt is being used for determining
    authenticated users.

    The basic strategy for process_{method}_input functions:

    Returns:
        status: (bool) : Success or failure
        data: (obj) :  if success, the data
                       if failure (explanation, status_code)

    That means the response.status_code can be tailored to fit your
    scenario.

    """

    method_decorators = [mock_jwt_required]

    def process_get_input(self, query, data, kwargs):
        """
        This function runs in the GET method with access to
        the Model.query object.
        """
        user_id = get_identity()
        if user_id:
            query = query.filter_by(owner_id=user_id)
            return {"status": True, "query": query, "data": data}

        return {"status": False, "message": "Not found", "status_code": 404}

    def process_post_input(self, data):
        """
        This function runs in the POST method with access to
        the data included with the request.
        """
        user_id = get_identity()
        owner_id = data.get("ownerId", None)
        if owner_id:
            if int(owner_id) == user_id:
                return {"status": True, "data": data}

        return {
            "status": False,
            "message": "The user id does not match the owner id",
            "status_code": 400,
        }

    def process_put_input(self, query, data, kwargs):
        """
        This function runs in the PUT method with access to
        the data included with the request.
        """
        user_id = get_identity()
        owner_id = data.get("ownerId", None)
        if owner_id:
            if int(owner_id) == user_id:
                return {"status": True, "query": query, "data": data}

        return {
            "status": False,
            "message": "The user id does not match the owner id",
            "status_code": 400,
        }

    def process_patch_input(self, query, data, kwargs):
        """
        This function runs in the PATCH method with access to
        the data included with the request.
        """
        user_id = get_identity()
        owner_id = data.get("ownerId", None)
        if owner_id:
            if int(owner_id) == user_id:
                return {"status": True, "data": data}

        return {
            "status": False,
            "message": "The user id does not match the owner id",
            "status_code": 400,
        }

    def process_delete_input(self, query, kwargs):
        """
        This function runs in the DELETE method.
        """
        user_id = get_identity()
        if user_id:
            query = query.filter_by(owner_id=user_id)
            return {"status": True, "query": query}

        return {"status": False, "message": "Not found", "status_code": 404}


class OwnerCollectionResource(CollectionModelResource):
    """
    Pretend that jwt is being used for determine authenticated users.
    """

    method_decorators = [mock_jwt_required]

    def process_get_input(self, query, data):
        user_id = get_identity()
        if user_id:
            query = query.filter_by(owner_id=user_id)
            return {"status": True, "query": query, "data": data}

        return {
            "status": False,
            "message": "The user id is not authorized",
            "status_code": 400,
        }

mock_jwt_required() provides the gatekeeping for access to the method. get_identity() extracts the authenticated user from the header. Together, they will provide the basis for inserting knowledge of the user in question to guide access to the right resources.

We are using this simple version, but a ‘real’ one, aside from actually being secure, might use admin and user roles and user status as other considerations.

Owner Resources

Once we create the subclass Owner resources to have the right combination of method decorators and processing of inputs, we will in turn subclass the owner resources.

class OrderCollection(OwnerCollectionResource):
    model_class = Order


class OrderResource(OwnerResource):
    model_class = Order


class OrderMetaCollection(MetaResource):
    resource_class = OrderCollection


class OrderMeta(MetaResource):
    resource_class = OrderResource

Create the Order Resources

Now we create the order resources. And, the code is minimal.

api.add_resource(OrderCollection, *OrderCollection.get_urls())
api.add_resource(OrderResource, *OrderResource.get_urls())
api.add_resource(OrderMetaCollection, *OrderMetaCollection.get_urls())
api.add_resource(OrderMeta, *OrderMeta.get_urls())


if __name__ == "__main__":
    app.run(debug=True, port=5003)

At this point we are ready to instantiate these resources with the API and fire up the app.

api.add_resource(OrderCollection, *OrderCollection.get_urls())
api.add_resource(OrderResource, *OrderResource.get_urls())
api.add_resource(OrderMetaCollection, *OrderMetaCollection.get_urls())
api.add_resource(OrderMeta, *OrderMeta.get_urls())


if __name__ == "__main__":
    app.run(debug=True)

Use the API

First, we will attempt to post an order.

  • Post without authentication

  • Post an order wrong login

  • Post an order that is correct

  • Get the order collection with the right user

Post without authentication

# post an order, but no authentication
curl http://localhost:5000/orders \
    -H "Content-Type: application/json" \
    -d '{"ownerId": 1, "description": "to do stuff"}'
{
    "message": "Unauthorized User"
}

Post An Order, Wrong Login

# post an order, but the wrong user
curl http://localhost:5000/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:2" \
    -d '{"ownerId": 1, "description": "to do stuff"}'
{
    "message": "The user id does not match the owner id"
}

Post An Order That Is Correct

# post an order, with the right user
curl http://localhost:5000/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1" \
    -d '{"ownerId": 1, "description": "to do stuff"}'
{
    "description": "to do stuff",
    "id": 1,
    "ownerId": 1,
    "statusId": 0,
    "orderedAt": "2020-08-27 10:37:47"
}

Get the Order Collection, No Authorization

# get orders, no authorization
curl http://localhost:5000/orders
{
    "message": "Unauthorized User"
}

Get the Order Collection, Wrong User

# get orders, with authorization, wrong user
curl http://localhost:5000/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:2"
{
    "Order": []
}

Get the Order Collection, Right User

# get orders, with authorization, right user
curl http://localhost:5000/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "Order": [
        {
            "description": "to do stuff",
            "id": 1,
            "ownerId": 1,
            "statusId": 0,
            "orderedAt": "2020-08-27 10:37:47"
        }
    ]
}

Order Meta Resource Information

# get meta data for OrderResource
curl http://localhost:5000/meta/orders/single \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "modelClass": "Order",
    "urlPrefix": "/",
    "baseUrl": "/orders",
    "methods": {
        "get": {
            "url": "/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "post": {
            "url": "/orders",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "put": {
            "url": "/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "patch": {
            "url": "/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "delete": {
            "url": "/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {}
            ]
        }
    },
    "table": {
        "Order": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "owner_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "ordered_at": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "status_id": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "xml": "Order"
        }
    }
}

Order Meta Collection Information

# get meta data for OrderCollectionResource
curl http://localhost:5000/meta/orders/collection \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "modelClass": "Order",
    "urlPrefix": "/",
    "baseUrl": "/orders",
    "methods": {
        "get": {
            "url": "/orders",
            "requirements": [
                "mock_jwt_required"
            ],
            "queryString": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "jobParams": {
                "orderBy": {
                    "type": "string",
                    "list": true
                },
                "maxPageSize": {
                    "type": "integer"
                },
                "offset": {
                    "type": "integer"
                },
                "debug": {
                    "type": "boolean"
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        }
                    }
                }
            ]
        }
    },
    "table": {
        "Order": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "owner_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "ordered_at": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "status_id": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "xml": "Order"
        }
    }
}

Next: Simple Owner App Revised

Simple Owner App Revised

We discussed how resources can be used to limit access by record as well as tables. Now we will look at customizing a resource to trigger an entry to a message queue within the context of simply saving a record.

In this example we will:

  • modify our Order model slightly

  • create a Job model

  • create an example of an after_commit() function that will run just after committing the order to the database

  • return the job details that has been created back to the frontend in place of the original order.

To keep the explanations short, we will focus on the changes made to the previous example.

As before, the code for this example is found in the examples section as

Models

The Order model will be used from the previous example with one change: It will maintain a relationship with the jobs that have been used to run whatever it is supposed to do.

class Order(db.Model):
    __tablename__ = "order"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    owner_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
    description = db.Column(db.String, nullable=False)
    ordered_at = db.Column(db.DateTime, default=datetime.now)
    status_id = db.Column(db.SmallInteger, default=0, nullable=True)

    jobs = db.relationship("Job", backref="order", lazy="immediate")

The jobs column provides access to the jobs associated with an order.

And a Job model is created that will store the particulars of the job.

class Job(db.Model):
    __tablename__ = "job"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    owner_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
    order_id = db.Column(db.Integer, db.ForeignKey("order.id"), nullable=False)
    started_at = db.Column(
        db.DateTime, server_default=db.func.now(), nullable=False
    )
    finished_at = db.Column(db.DateTime)
    status_id = db.Column(db.SmallInteger, default=0, nullable=True)


db.create_all()

Order Resources

Now we recreate the order resources. To reflect the fact we are getting serious about the API and that it is the second version will add a url_prefix to the resources.

class OrderCollection(OwnerCollectionResource):
    model_class = Order
    url_prefix = "/api/v2"

After Commit Function

The after_commit is the key to this implementation. It runs just after the object has been commited to the database. In this case, we will use the order object as the basis for creating a job object, then pack the job off to a queue.

def create_job(self, order, status_code):
    """
    This function creates a processing job from an order.

    It runs after the order is saved to the database, then
    creates a job and submits it to processing.

    Args:
        order: (obj) : The order that is to be processed.
        status_code: (int) :
    Returns:
        return_status: (bool) : True to coninue,
            False  to return just a message
        job: (obj) : The job that is created.
        status_code: (int) : The new response status
        code.
    """

    job = Job(owner_id=order.owner_id, order_id=order.id).save()
    status_code = 202
    # this is where you send the job to queue

    return True, job, status_code

Revised Order Resource

Our revised OrderResource is below:

class OrderResource(OwnerResource):
    model_class = Order
    url_prefix = "/api/v2"

    serial_fields = {
        "post": {Job: ["id", "started_at", "status_id"]},
        "put": {Job: ["id", "started_at", "status_id"]},
    }

    after_commit = {"post": create_job, "put": create_job}

You can see that the after_commit function that we created, create_job is associated with the POST and PUT methods. The other methods are unchanged by this process.

By default, the serial_fields and serial_field_relations will use the settings of the current object as the basis for returning data to the frontend. So, the GET and PATCH methods will automatically return Order data and POST and PUT will use the Job serial fields.

For no good reason, we will change the serial fields for Jobs to return only only the id, start time and status.

Note

If you create an after_commit function that returns a different class than the one you with which you started, that new class must be added to the serial_fields class variable as we have done in the example. Otherwise, the meta resource has no way of knowing that you have made this choice. If you want to simply return the default serial fields for your replacement class then use the form such as:

serial_fields = {
    "post": {Job: None},
    "put":  {Job: None},
}

which will guide the meta processes to select the right default values.

Job Resources

We can also monitor our jobs by creating Job resources.

class JobCollection(OwnerCollectionResource):
    model_class = Job
    url_prefix = "/api/v2"


class JobResource(OwnerResource):
    model_class = Job
    url_prefix = "/api/v2"


class JobMetaCollection(MetaResource):
    resource_class = JobCollection


class JobMeta(MetaResource):
    resource_class = JobResource

With our revisions we will start again.

api.add_resource(OrderCollection, *OrderCollection.get_urls())
api.add_resource(OrderResource, *OrderResource.get_urls())
api.add_resource(OrderMetaCollection, *OrderMetaCollection.get_urls())
api.add_resource(OrderMeta, *OrderMeta.get_urls())

api.add_resource(JobCollection, *JobCollection.get_urls())
api.add_resource(JobResource, *JobResource.get_urls())
api.add_resource(JobMetaCollection, *JobMetaCollection.get_urls())
api.add_resource(JobMeta, *JobMeta.get_urls())


if __name__ == "__main__":
    app.run(debug=True, port=5004)

Use the API

Create an Order, Receive a Job

# post an order, receive a job
curl http://localhost:5000/api/v2/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1" \
    -d '{"ownerId": 1, "description": "to do stuff"}'
{
    "id": 1,
    "startedAt": "2020-08-27 17:38:03",
    "statusId": 0
}

We can see the serialized fields that we selected for a job.

Update an Order, Receive a Job

# put an order, receive a job
curl --request PUT http://localhost:5000/api/v2/orders/1 \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1" \
    -d '{"ownerId": 1, "description": "to do different stuff"}'
{
    "id": 2,
    "startedAt": "2020-08-27 17:38:03",
    "statusId": 0
}

Now we have run the same order a second time after updating the description.

Get Orders

# get orders
curl http://localhost:5000/api/v2/orders \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "Order": [
        {
            "description": "to do different stuff",
            "orderedAt": "2020-08-27 10:38:03",
            "statusId": 0,
            "ownerId": 1,
            "id": 1,
            "jobs": [
                {
                    "startedAt": "2020-08-27 17:38:03",
                    "finishedAt": null,
                    "statusId": 0,
                    "ownerId": 1,
                    "orderId": 1,
                    "id": 1
                },
                {
                    "startedAt": "2020-08-27 17:38:03",
                    "finishedAt": null,
                    "statusId": 0,
                    "ownerId": 1,
                    "orderId": 1,
                    "id": 2
                }
            ]
        }
    ]
}

Notice that the full list of fields for the related jobs still shows. If we wanted to always show a restricted list of fields for jobs, the place to change that is upstream at the Job model by settinng its SERIAL_FIELDS.

Get Jobs

# get jobs
curl http://localhost:5000/api/v2/jobs \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "Job": [
        {
            "order": {
                "description": "to do different stuff",
                "orderedAt": "2020-08-27 10:38:03",
                "statusId": 0,
                "ownerId": 1,
                "id": 1
            },
            "startedAt": "2020-08-27 17:38:03",
            "finishedAt": null,
            "statusId": 0,
            "ownerId": 1,
            "orderId": 1,
            "id": 1
        },
        {
            "order": {
                "description": "to do different stuff",
                "orderedAt": "2020-08-27 10:38:03",
                "statusId": 0,
                "ownerId": 1,
                "id": 1
            },
            "startedAt": "2020-08-27 17:38:03",
            "finishedAt": null,
            "statusId": 0,
            "ownerId": 1,
            "orderId": 1,
            "id": 2
        }
    ]
}

Order Meta Resource Information

The meta information for the Order Resource is much the same as before. Lengthy as it is, the point of interest is found in the response variables for POST and PUT which shows the appropriate meta information for the Job serial fields selected.

# get meta data for OrderResource
curl http://localhost:5000/api/v2/meta/orders/single \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "modelClass": "Order",
    "urlPrefix": "/api/v2",
    "baseUrl": "/api/v2/orders",
    "methods": {
        "get": {
            "url": "/api/v2/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "jobs": {
                            "readOnly": false,
                            "relationship": {
                                "type": "list",
                                "entity": "Job",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "orderId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "order.id",
                                        "info": {}
                                    },
                                    "startedAt": {
                                        "type": "date-time",
                                        "nullable": false,
                                        "server_default": {
                                            "for_update": false,
                                            "arg": "db.func.now()",
                                            "reflected": false
                                        },
                                        "info": {}
                                    },
                                    "finishedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        },
        "post": {
            "url": "/api/v2/orders",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                },
                "jobs": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "Job",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "ownerId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "user.id",
                                "info": {}
                            },
                            "orderId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "order.id",
                                "info": {}
                            },
                            "startedAt": {
                                "type": "date-time",
                                "nullable": false,
                                "server_default": {
                                    "for_update": false,
                                    "arg": "db.func.now()",
                                    "reflected": false
                                },
                                "info": {}
                            },
                            "finishedAt": {
                                "type": "date-time",
                                "nullable": true,
                                "info": {}
                            },
                            "statusId": {
                                "type": "integer",
                                "format": "int8",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": 0,
                                    "is_clause_element": false,
                                    "is_callable": false,
                                    "is_scalar": true
                                },
                                "info": {}
                            }
                        }
                    }
                }
            },
            "responses": [
                {
                    "fields": {
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "put": {
            "url": "/api/v2/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                },
                "jobs": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "Job",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "ownerId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "user.id",
                                "info": {}
                            },
                            "orderId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "order.id",
                                "info": {}
                            },
                            "startedAt": {
                                "type": "date-time",
                                "nullable": false,
                                "server_default": {
                                    "for_update": false,
                                    "arg": "db.func.now()",
                                    "reflected": false
                                },
                                "info": {}
                            },
                            "finishedAt": {
                                "type": "date-time",
                                "nullable": true,
                                "info": {}
                            },
                            "statusId": {
                                "type": "integer",
                                "format": "int8",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": 0,
                                    "is_clause_element": false,
                                    "is_callable": false,
                                    "is_scalar": true
                                },
                                "info": {}
                            }
                        }
                    }
                }
            },
            "responses": [
                {
                    "fields": {
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        }
                    }
                }
            ]
        },
        "patch": {
            "url": "/api/v2/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "orderedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                },
                "jobs": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "Job",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "ownerId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "user.id",
                                "info": {}
                            },
                            "orderId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "order.id",
                                "info": {}
                            },
                            "startedAt": {
                                "type": "date-time",
                                "nullable": false,
                                "server_default": {
                                    "for_update": false,
                                    "arg": "db.func.now()",
                                    "reflected": false
                                },
                                "info": {}
                            },
                            "finishedAt": {
                                "type": "date-time",
                                "nullable": true,
                                "info": {}
                            },
                            "statusId": {
                                "type": "integer",
                                "format": "int8",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": 0,
                                    "is_clause_element": false,
                                    "is_callable": false,
                                    "is_scalar": true
                                },
                                "info": {}
                            }
                        }
                    }
                }
            },
            "responses": [
                {
                    "fields": {
                        "description": {
                            "type": "string",
                            "nullable": false,
                            "info": {}
                        },
                        "orderedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": "datetime.now",
                                "is_clause_element": false,
                                "is_callable": true,
                                "is_scalar": false
                            },
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "jobs": {
                            "readOnly": false,
                            "relationship": {
                                "type": "list",
                                "entity": "Job",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "orderId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "order.id",
                                        "info": {}
                                    },
                                    "startedAt": {
                                        "type": "date-time",
                                        "nullable": false,
                                        "server_default": {
                                            "for_update": false,
                                            "arg": "db.func.now()",
                                            "reflected": false
                                        },
                                        "info": {}
                                    },
                                    "finishedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        },
        "delete": {
            "url": "/api/v2/orders/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {}
            ]
        }
    },
    "table": {
        "Order": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "owner_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "nullable": false,
                    "info": {}
                },
                "ordered_at": {
                    "type": "date-time",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": "datetime.now",
                        "is_clause_element": false,
                        "is_callable": true,
                        "is_scalar": false
                    },
                    "info": {}
                },
                "status_id": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                },
                "jobs": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "Job",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "owner_id": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "user.id",
                                "info": {}
                            },
                            "order_id": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "order.id",
                                "info": {}
                            },
                            "started_at": {
                                "type": "date-time",
                                "nullable": false,
                                "server_default": {
                                    "for_update": false,
                                    "arg": "db.func.now()",
                                    "reflected": false
                                },
                                "info": {}
                            },
                            "finished_at": {
                                "type": "date-time",
                                "nullable": true,
                                "info": {}
                            },
                            "status_id": {
                                "type": "integer",
                                "format": "int8",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": 0,
                                    "is_clause_element": false,
                                    "is_callable": false,
                                    "is_scalar": true
                                },
                                "info": {}
                            }
                        }
                    }
                }
            },
            "xml": "Order"
        }
    }
}

Job Meta Information

# get meta data for JobResource
curl http://localhost:5000/api/v2/meta/jobs/single \
    -H "Content-Type: application/json" \
    -H "Authorization: User:1"
{
    "modelClass": "Job",
    "urlPrefix": "/api/v2",
    "baseUrl": "/api/v2/jobs",
    "methods": {
        "get": {
            "url": "/api/v2/jobs/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "order": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Order",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "orderedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": "datetime.now",
                                            "is_clause_element": false,
                                            "is_callable": true,
                                            "is_scalar": false
                                        },
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "finishedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "orderId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "order.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "post": {
            "url": "/api/v2/jobs",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "orderId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "order.id",
                    "info": {}
                },
                "startedAt": {
                    "type": "date-time",
                    "nullable": false,
                    "server_default": {
                        "for_update": false,
                        "arg": "db.func.now()",
                        "reflected": false
                    },
                    "info": {}
                },
                "finishedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "order": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Order",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "orderedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": "datetime.now",
                                            "is_clause_element": false,
                                            "is_callable": true,
                                            "is_scalar": false
                                        },
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "finishedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "orderId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "order.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "put": {
            "url": "/api/v2/jobs/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "orderId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "order.id",
                    "info": {}
                },
                "startedAt": {
                    "type": "date-time",
                    "nullable": false,
                    "server_default": {
                        "for_update": false,
                        "arg": "db.func.now()",
                        "reflected": false
                    },
                    "info": {}
                },
                "finishedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "order": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Order",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "orderedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": "datetime.now",
                                            "is_clause_element": false,
                                            "is_callable": true,
                                            "is_scalar": false
                                        },
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "finishedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "orderId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "order.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "patch": {
            "url": "/api/v2/jobs/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "ownerId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "orderId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "order.id",
                    "info": {}
                },
                "startedAt": {
                    "type": "date-time",
                    "nullable": false,
                    "server_default": {
                        "for_update": false,
                        "arg": "db.func.now()",
                        "reflected": false
                    },
                    "info": {}
                },
                "finishedAt": {
                    "type": "date-time",
                    "nullable": true,
                    "info": {}
                },
                "statusId": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                }
            },
            "responses": [
                {
                    "fields": {
                        "order": {
                            "readOnly": true,
                            "relationship": {
                                "type": "single",
                                "entity": "Order",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "ownerId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "user.id",
                                        "info": {}
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "orderedAt": {
                                        "type": "date-time",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": "datetime.now",
                                            "is_clause_element": false,
                                            "is_callable": true,
                                            "is_scalar": false
                                        },
                                        "info": {}
                                    },
                                    "statusId": {
                                        "type": "integer",
                                        "format": "int8",
                                        "nullable": true,
                                        "default": {
                                            "for_update": false,
                                            "arg": 0,
                                            "is_clause_element": false,
                                            "is_callable": false,
                                            "is_scalar": true
                                        },
                                        "info": {}
                                    }
                                }
                            }
                        },
                        "startedAt": {
                            "type": "date-time",
                            "nullable": false,
                            "server_default": {
                                "for_update": false,
                                "arg": "db.func.now()",
                                "reflected": false
                            },
                            "info": {}
                        },
                        "finishedAt": {
                            "type": "date-time",
                            "nullable": true,
                            "info": {}
                        },
                        "statusId": {
                            "type": "integer",
                            "format": "int8",
                            "nullable": true,
                            "default": {
                                "for_update": false,
                                "arg": 0,
                                "is_clause_element": false,
                                "is_callable": false,
                                "is_scalar": true
                            },
                            "info": {}
                        },
                        "ownerId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "user.id",
                            "info": {}
                        },
                        "orderId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "foreign_key": "order.id",
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        }
                    }
                }
            ]
        },
        "delete": {
            "url": "/api/v2/jobs/<int:id>",
            "requirements": [
                "mock_jwt_required"
            ],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                }
            },
            "responses": [
                {}
            ]
        }
    },
    "table": {
        "Job": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "owner_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "user.id",
                    "info": {}
                },
                "order_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "foreign_key": "order.id",
                    "info": {}
                },
                "started_at": {
                    "type": "date-time",
                    "nullable": false,
                    "server_default": {
                        "for_update": false,
                        "arg": "db.func.now()",
                        "reflected": false
                    },
                    "info": {}
                },
                "finished_at": {
                    "type": "date-time",
                    "nullable": true,
                    "info": {}
                },
                "status_id": {
                    "type": "integer",
                    "format": "int8",
                    "nullable": true,
                    "default": {
                        "for_update": false,
                        "arg": 0,
                        "is_clause_element": false,
                        "is_callable": false,
                        "is_scalar": true
                    },
                    "info": {}
                },
                "order": {
                    "readOnly": true,
                    "relationship": {
                        "type": "single",
                        "entity": "Order",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "owner_id": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "user.id",
                                "info": {}
                            },
                            "description": {
                                "type": "string",
                                "nullable": false,
                                "info": {}
                            },
                            "ordered_at": {
                                "type": "date-time",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": "datetime.now",
                                    "is_clause_element": false,
                                    "is_callable": true,
                                    "is_scalar": false
                                },
                                "info": {}
                            },
                            "status_id": {
                                "type": "integer",
                                "format": "int8",
                                "nullable": true,
                                "default": {
                                    "for_update": false,
                                    "arg": 0,
                                    "is_clause_element": false,
                                    "is_callable": false,
                                    "is_scalar": true
                                },
                                "info": {}
                            }
                        }
                    }
                }
            },
            "xml": "Job"
        }
    }
}

Next: Parent Post - Child Records

Parent Post - Child Records

Since some kinds of data by their very nature are really a set consisting of a parent record and a list of child records. In this section we will look at an example for efficiently handling that.

Our example is that of an invoice. Suppose an invoice is created with 20 items. If we follow a purely REST approach, we would create a parent invoice followed by another 20 trips to the database for each item. Or, we could implement a collection POST resource for the invoice items and only have two trips. In that case, one trip for the parent and one for the set of invoice items.

With Flask-RESTful-DBBase, we will do it in one trip in the following example.

As before, the code for this example is found in the examples section as

Create the database models

When we create our two tables to make up an invoice, note how the invoice_items relation is specified with backref. That means that the InvoiceItem will also have a field called invoice. And, it invokes a bidirectional update capability between an invoice and the set of invoice items.

class Invoice(db.Model):
    __tablename__ = "invoice"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    user_id = db.Column(db.Integer, nullable=False)
    invoice_date = db.Column(db.Date, nullable=False)

    # this is the critical part, it requires either backref or
    #   back_populates
    invoice_items = db.relationship("InvoiceItem", backref="invoice")


class InvoiceItem(db.Model):
    __tablename__ = "invoice_item"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    invoice_id = db.Column(
        db.Integer, db.ForeignKey("invoice.id"), nullable=False
    )
    part_code = db.Column(db.String, nullable=False)
    units = db.Column(db.Integer, nullable=False)
    unit_price = db.Column(db.Float(precision=2), nullable=False)


db.create_all()

Create the Invoice Resource

The InvoiceResource is pretty standard. There is one twist, unrelated to the topic at hand. Because we are using Sqlite3 as the default database, the invoice date that is sent as a string is not properly processed by Sqlite3. Converting a date string to a datatime object takes some time, so if your database automatically does this, there is no need for the extra step.

In this case, however, we will use use_date_conversion to signify that the API should do the conversion for us.

class InvoiceResource(ModelResource):
    model_class = Invoice

    # necessary only if the database does not understand
    #   dates in string form -- such as Sqlite3
    use_date_conversions = True

Finish the Rest of the Invoice Resources

The following resources are consistent with the other examples with nothing else that is special.

class InvoiceCollectionResource(CollectionModelResource):
    model_class = Invoice


class InvoiceItemResource(ModelResource):
    model_class = InvoiceItem


class InvoiceItemCollectionResource(ModelResource):
    model_class = InvoiceItem


# create meta resources
class InvoiceMeta(MetaResource):
    resource_class = InvoiceResource


class InvoiceMetaCollection(MetaResource):
    resource_class = InvoiceResource


class InvoiceItemMeta(MetaResource):
    resource_class = InvoiceItemResource


class InvoiceItemMetaCollection(MetaResource):
    resource_class = InvoiceItemResource

Add the Resources to the API

Finally, as before the resources are added to the API and we are ready for business.

api.add_resource(
    InvoiceCollectionResource, *InvoiceCollectionResource.get_urls()
)
api.add_resource(InvoiceResource, *InvoiceResource.get_urls())
api.add_resource(
    InvoiceItemCollectionResource, *InvoiceItemCollectionResource.get_urls()
)
api.add_resource(InvoiceItemResource, *InvoiceResource.get_urls())

api.add_resource(InvoiceMeta, *InvoiceMeta.get_urls())
api.add_resource(InvoiceMetaCollection, *InvoiceMetaCollection.get_urls())
api.add_resource(InvoiceItemMeta, *InvoiceItemMeta.get_urls())
api.add_resource(
    InvoiceItemMetaCollection, *InvoiceItemMetaCollection.get_urls()
)


if __name__ == "__main__":
    app.run(debug=True, port=5002)

Use the API

Create an Invoice

We will now create an invoice. The data consists of both the invoice portion and a list of line items that are to be added to the invoice. See how the invoice_id is left off of the invoice items data. While it is unknowable until the invoice is created, SQLAlchemy automatically fills it in when posting the line items.

# post an invoice
curl http://localhost:5000/invoices \
    -H "Content-Type: application/json" \
    -d '{
  "userId": 1,
  "invoiceDate": "2020-08-27",
  "invoiceItems": [
    {
      "partCode": "111",
      "units": "1",
      "unitPrice": 20.0
    },
    {
      "partCode": "222",
      "units": "5",
      "unitPrice": 15.0
    }
  ]
}'
{
    "invoiceItems": [
        {
            "units": 1,
            "unitPrice": 20.0,
            "partCode": "111",
            "invoiceId": 1,
            "id": 1
        },
        {
            "units": 5,
            "unitPrice": 15.0,
            "partCode": "222",
            "invoiceId": 1,
            "id": 2
        }
    ],
    "invoiceDate": "2020-08-27",
    "id": 1,
    "userId": 1
}

Get an Invoice:

And we can GET an invoice with its related line items.

# get an invoice
curl http://localhost:5000/invoices/1 \
    -H "Content-Type: application/json"
{
    "invoiceItems": [
        {
            "units": 1,
            "unitPrice": 20.0,
            "partCode": "111",
            "invoiceId": 1,
            "id": 1
        },
        {
            "units": 5,
            "unitPrice": 15.0,
            "partCode": "222",
            "invoiceId": 1,
            "id": 2
        }
    ],
    "invoiceDate": "2020-08-27",
    "id": 1,
    "userId": 1
}

Meta Information for Invoice Post

By examining the meta information for an invoice we can also determine whether we have the correct settings to create invoice items with the invoice.

If you look at input section and invoiceItems, it shows readOnly as False, meaning that it is updatable.

# meta info for POST
curl http://localhost:5000/meta/invoices/single?method=post \
    -H "Content-Type: application/json"
{
    "modelClass": "Invoice",
    "urlPrefix": "/",
    "baseUrl": "/invoices",
    "methods": {
        "post": {
            "url": "/invoices",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "userId": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "invoiceDate": {
                    "type": "date",
                    "nullable": false,
                    "info": {}
                },
                "invoiceItems": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "InvoiceItem",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "invoiceId": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "invoice.id",
                                "info": {}
                            },
                            "partCode": {
                                "type": "string",
                                "nullable": false,
                                "info": {}
                            },
                            "units": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "info": {}
                            },
                            "unitPrice": {
                                "type": "float",
                                "nullable": false,
                                "info": {}
                            }
                        }
                    }
                }
            },
            "responses": [
                {
                    "fields": {
                        "invoiceItems": {
                            "readOnly": false,
                            "relationship": {
                                "type": "list",
                                "entity": "InvoiceItem",
                                "fields": {
                                    "id": {
                                        "type": "integer",
                                        "format": "int32",
                                        "primary_key": true,
                                        "nullable": true,
                                        "info": {}
                                    },
                                    "invoiceId": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "foreign_key": "invoice.id",
                                        "info": {}
                                    },
                                    "partCode": {
                                        "type": "string",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "units": {
                                        "type": "integer",
                                        "format": "int32",
                                        "nullable": false,
                                        "info": {}
                                    },
                                    "unitPrice": {
                                        "type": "float",
                                        "nullable": false,
                                        "info": {}
                                    }
                                }
                            }
                        },
                        "invoiceDate": {
                            "type": "date",
                            "nullable": false,
                            "info": {}
                        },
                        "id": {
                            "type": "integer",
                            "format": "int32",
                            "primary_key": true,
                            "nullable": true,
                            "info": {}
                        },
                        "userId": {
                            "type": "integer",
                            "format": "int32",
                            "nullable": false,
                            "info": {}
                        }
                    }
                }
            ]
        }
    },
    "table": {
        "Invoice": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "user_id": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                },
                "invoice_date": {
                    "type": "date",
                    "nullable": false,
                    "info": {}
                },
                "invoice_items": {
                    "readOnly": false,
                    "relationship": {
                        "type": "list",
                        "entity": "InvoiceItem",
                        "fields": {
                            "id": {
                                "type": "integer",
                                "format": "int32",
                                "primary_key": true,
                                "nullable": true,
                                "info": {}
                            },
                            "invoice_id": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "foreign_key": "invoice.id",
                                "info": {}
                            },
                            "part_code": {
                                "type": "string",
                                "nullable": false,
                                "info": {}
                            },
                            "units": {
                                "type": "integer",
                                "format": "int32",
                                "nullable": false,
                                "info": {}
                            },
                            "unit_price": {
                                "type": "float",
                                "nullable": false,
                                "info": {}
                            }
                        }
                    }
                }
            },
            "xml": "Invoice"
        }
    }
}

Caveat

This ability to post child records with the parent is only implemented in the POST method. It is probably more efficient to update individual line items if using PUT or PATCH avoiding reconciliation issues related to alterations of existing records.

Limited Method Resources

Sometimes a pure REST approach does not fit the problem. For example, suppose a URL primarily represents an action. If the problem still interacts with the database in some fashion, it may be appropriate to create a resource that is limited in the methods that it exposes.

Create Resource

The create_resource function can create a resource with a custom configuration. The basic principals:

  • Specify the name of the resource.

  • Use a prototype resource class to model it on.

  • Specify a model_class to use.

  • Specify which methods to implement. If there is a process_{method}_input function in your prototype resource, it will be included.

  • You can set url_prefix and url_name if necessary.

  • You can overwrite any of the class variables to customize your new resource further by including a dict of class variables.

  • Finally, the new resource will be created.

PostOnlyResource = create_resource(
    name="PostOnlyResource",
    resource_class=ModelResource,
    model_class=Whatever,
    methods=['post'],
    url_prefix=None,
    url_name=None,
    class_vars={
        "after_commmit": {
            "post": my_procedure
        }
    },
)

Suggested Uses

  • /register: POST using a User class model. A confirmation email is sent. A message is returned with instructions in place of the posted user data.

  • A command is run that uses deserialization / validation services but goes on to execute the command.

Note

One thing to consider regarding these alterations is the degree of change wanted from a REST format. For example, a POST method with REST excludes posting an existing data item. Flask-RESTful-DBBase automatically verifies that there is no existing record as a benefit.

If that is not suitable for the application, another alternative would be to create a resource from the proto resource, DBBaseResource. With it are functions for screening data, etc., and you could create your own POST method.

Example

As before, the code for this example is found in the examples section as

To give a concrete example, we will create a resource limited to a POST method. The following shows an example of the usual initialization with the exception of the generator.create_resource program. This program accepts a source resource. From it create_resource will generate a resource in a custom configuration.

There are two additional features imported, create_resource and MetaDoc.

from flask import Flask
from flask_restful import Api
from flask_restful_dbbase import DBBase
from flask_restful_dbbase.resources import ModelResource, MetaResource
from flask_restful_dbbase.generator import create_resource
from flask_restful_dbbase.doc_utils import MetaDoc, MethodDoc

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

api = Api(app)
db = DBBase(app)

The table created is minimal for this example.

class AModel(db.Model):
    __tablename__ = "amodel"

    id = db.Column(db.Integer, nullable=True, primary_key=True)
    description = db.Column(db.String(80), unique=True, nullable=False)
    num_variable = db.Column(db.Integer, nullable=False)


db.create_all()

We will divert processing of a POST to exit just after a commit to the database. To that end, the following is a minimal after_commit function that will execute.

The sign that a normal return of the posted data does not happen can be seen at the return. The return tuple starts with a False.

def after_commit(self, item, status_code):
    """
    This will be used in an after_commit function.

    In this case, the process is diverted to a
    message, new status code and the method exits with
    this message.
    """
    # processing takes place here
    payload = {
        "message": "This is no longer a REST resource. We can do anything.",
        "data": item.to_dict(),
    }

    return False, payload, 419

Having defined our exit function, we can now create the resource itself. The following shows that creation. The parameters give some flexibility of output.

  • The source resource class is ModelResource in this case, although it could be a subclassed resource.

  • Only the POST method is implemented.

  • Like any ModelResource, the url_prefix and url_name can be set explcitly.

  • Finally, the class_vars provides a means to set any attribute of the new class. In this case we will use that feature to set the after_commit function created above.

PostOnlyResource = create_resource(
    name="PostOnlyResource",
    resource_class=ModelResource,
    model_class=AModel,
    methods=["post"],
    url_prefix="/",
    url_name="a-model-command",
    class_vars={
        "after_commit": {"post": after_commit},
    },
)

Because we have caused the resource to give output outside of a standard REST response, meta information associated with this method does not fit the new reality. To accommodate this, we will use a new feature, the meta_doc attribute. This is an instance of the MetaDoc found in utils.py. It presents a structure to place documentation for functions, and also a flag to exclude the standard responses when it is not relevant.

# response associated with a POST.
meta_doc = MetaDoc(
    resource_class=PostOnlyResource,
    methods=[
        MethodDoc(
            method="post",
            after_commit="Here we can say a few words about the process",
            use_default_response=False,
            responses=[{"messsage": "Here we can describe the response"}],
        )
    ],
)
PostOnlyResource.meta_doc = meta_doc


class PostOnlyMetaResource(MetaResource):
    resource_class = PostOnlyResource

The following shows the addition of the resources and startup of the app.

api.add_resource(PostOnlyResource, *PostOnlyResource.get_urls())

api.add_resource(PostOnlyMetaResource, *PostOnlyMetaResource.get_urls())


if __name__ == "__main__":
    app.run(debug=True, port=5001)

Using the API

We POST data to the URL. Data deserialization and validation takes place as usual. And, because this is an after_commit function, it is saved to the database. The result shows the non-REST response to the entry.

# create an entry
curl http://localhost:5000/a-model-command \
    -H "Content-Type: application/json" \
    -d '{"description": "A test", "num_variable": 42}'
{
    "message": "This is no longer a REST resource. We can do anything.",
    "data": {
        "numVariable": 42,
        "description": "A test",
        "id": 1
    }
}

Generating the meta information can reflect that the usual output has been replaced with our new message.

# create an entry
curl http://localhost:5000/meta/a-model-command/single \
    -H "Content-Type: application/json"
{
    "modelClass": "AModel",
    "urlPrefix": "/",
    "baseUrl": "/a-model-command",
    "methods": {
        "post": {
            "url": "/a-model-command",
            "requirements": [],
            "input": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "maxLength": 80,
                    "nullable": false,
                    "unique": true,
                    "info": {}
                },
                "numVariable": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                }
            },
            "after_commit": "Here we can say a few words about the process",
            "responses": [
                {
                    "messsage": "Here we can describe the response"
                }
            ]
        }
    },
    "table": {
        "AModel": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32",
                    "primary_key": true,
                    "nullable": true,
                    "info": {}
                },
                "description": {
                    "type": "string",
                    "maxLength": 80,
                    "nullable": false,
                    "unique": true,
                    "info": {}
                },
                "num_variable": {
                    "type": "integer",
                    "format": "int32",
                    "nullable": false,
                    "info": {}
                }
            },
            "xml": "AModel"
        }
    }
}