Inheritance¶
By default, re-using SQLAlchemy models through inheritance is not simple as
SQLAlchemy uses inheritance for other purposes. OpenAlchemy supports
model inheritance using the allOf
statement from OpenAPI. Currently
column and model inheritance is supported.
See also
- SQLAlchemy inheritance documentation
Documentation for SQLAlchemy inheritance.
Column Inheritance¶
For columns, the main purpose of using inheritance through allOf
is to
re-use elements of a base column definition but customize certain properties.
For example, you might have an integer id
column for many models that
is quite similar except for the description. Or you might have a string
name
column on many models but where the description and example might
differ. For example, the following specification defines a base schema for the
id
and name
columns and re-uses them for the Employee
and Division
models with some changes to the description, example or
both.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | openapi: "3.0.0" info: title: Test Schema description: API to illustrate allOf usage for columns. version: "0.1" paths: /employee: get: summary: Used to retrieve all employees. responses: 200: description: Return all employees from the database. content: application/json: schema: type: array items: "$ref": "#/components/schemas/Employee" /division: get: summary: Used to retrieve all divisions. responses: 200: description: Return all divisions from the database. content: application/json: schema: type: array items: "$ref": "#/components/schemas/Division" components: schemas: IdBase: type: integer description: Base for the id schema of an object. example: 0 x-primary-key: true NameBase: type: string description: The name of the object. example: Object 1. Employee: description: Person that works for a company. type: object x-tablename: employee properties: id: allOf: - "$ref": "#/components/schemas/IdBase" - description: Unique identifier for the employee. name: allOf: - "$ref": "#/components/schemas/NameBase" - description: The name of the employee. example: David Andersson salary: type: number description: The amount of money the employee is paid. example: 1000000.00 Division: description: A part of a company. type: object x-tablename: division properties: id: allOf: - "$ref": "#/components/schemas/IdBase" - description: Unique identifier for the division. name: allOf: - "$ref": "#/components/schemas/NameBase" - description: The name of the division. example: Engineering. |
The SQLAlchemy models might then look like:
1 2 3 | from open_alchemy import init_yaml init_yaml("column-example-spec.yml", models_filename="column_models_auto.py") |
Any duplicate properties are overridden by subsequent entries in allOf
.
For example, if all entries have the description
property, the
description
from the last entry is actually used.
Model Inheritance¶
A similar feature is also supported for models. This allows, for example, to
define a base model that has an id
and name
. Then other models
with id
and name
columns can use the allOf
feature to
copy those columns and add any model specific columns. This reduces duplication
in specifications. For example, the following specification defines an
IdNameBase
model with id
and name
columns.
Employee
and Division
also required the columns but the
description
and x-tablename needs to be different. By using
allOf
they can copy the id
and name
columns from
IdNameBase
and define their own description
and
tablename
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | openapi: "3.0.0" info: title: Test Schema description: API to illustrate OpenAlchemy MVP. version: "0.1" paths: /employee: get: summary: Used to retrieve all employees. responses: 200: description: Return all employees from the database. content: application/json: schema: type: array items: "$ref": "#/components/schemas/Employee" /division: get: summary: Used to retrieve all divisions. responses: 200: description: Return all divisions from the database. content: application/json: schema: type: array items: "$ref": "#/components/schemas/Division" components: schemas: IdNameBase: description: Base schema with an id and name column. type: object properties: id: type: integer description: Unique identifier for the object. example: 0 x-primary-key: true name: type: string description: The name of the object. example: Object 1. Division: allOf: - "$ref": "#/components/schemas/IdNameBase" - description: A part of a company. x-tablename: division Employee: allOf: - "$ref": "#/components/schemas/IdNameBase" - description: Person that works for a company. x-tablename: employee type: object properties: salary: type: number description: The amount of money the employee is paid. example: 1000000.00 |
The SQLAlchemy models might then look like:
1 2 3 | from open_alchemy import init_yaml init_yaml("model-example-spec.yml", models_filename="model_models_auto.py") |
Similar rules as for columns apply for duplicate properties in allOf
entries with one difference. The required
property is treated as an
aggregate of all entries. For example, if the first entry has id
and
the second entry has name
in the required list, the final specification
has both id
and name
in the required list.
See also
References shows how to reference to other schemas.
Joined Table Inheritance¶
SQLAlchemy includes a feature where a model class hierarchy is implemented by
giving each class it’s own table. For example, an Employee
might have
an id
, name
and type
. Then a Manager
model
might be defined that derives from Employee
and adds the
manager_data
column. This is also supported by OpenAlchemy through a
combination of the x-inherits
and x-kwargs
extension
properties. The x-kwargs
has already been discussed here:
Miscellaneous Model Arguments and will be used to define some special model parameters to
instruct SQLAlchemy how to map Manager
to Employee
.
x-inherits
¶
The x-inherits
extension property is used to indicate to OpenAlchemy
that a schema inherits from another schema. The following values are supported:
boolean
:true
: Indicates that the schema inherits from the closest schema that it has a$ref
to that can be constructed. Object schemas with thex-tablename
orx-inherits
are those that generally can be constructed.false
: Indicates that the schema does not inherit.
string
: The name of the parent schema. The schema must have a$ref
that links it to the schema with the name ofx-inherits
.
In any case, if a schema inherits from another schema, there must be a
$ref
linking the two schemas. That can be contained in allOf
and could be behind any number of nested $ref
.
See also
References shows how to reference to other schemas.
x-kwargs
¶
The required __mapper_args__
must be added using x-kwargs
. For
example, for Employee
:
1 2 3 4 5 6 7 | Employee: type: object ... x-kwargs: __mapper_args__: polymorphic_on: type polymorphic_identity: employee |
And for Manager
:
1 2 3 4 5 6 | Manager: type: object ... x-kwargs: __mapper_args__: polymorphic_identity: manager |
Example¶
The following shows the Employee
and Manager
schemas required
to define joined table inheritance in OpenAlchemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | Employee: type: object x-tablename: employee properties: id: type: integer x-primary-key: true ... type: type: string description: The type of the employee. example: employee x-kwargs: __mapper_args__: polymorphic_on: type polymorphic_identity: employee Manager: allOf: - $ref: "#/components/schemas/Employee" - x-inherits: true x-tablename: manager type: object properties: id: type: integer x-primary-key: true x-foreign-key: employee.id manager_data: type: string x-kwargs: __mapper_args__: polymorphic_identity: manager |
See also
- Joined Table
Full example for joined table inheritance.
- SQLAlchemy joined table inheritance documentation
Documentation for SQLAlchemy joined table inheritance.
Single Table Inheritance¶
Single table inheritance is very similar to Joined Table Inheritance
with the difference that all classes are linked to the same table and there is
no foreign key relationship between the models. The following shows the
Employee
and Manager
schemas required to define single table
inheritance in OpenAlchemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Employee: type: object x-tablename: employee properties: id: type: integer x-primary-key: true ... type: type: string description: The type of the employee. example: employee x-kwargs: __mapper_args__: polymorphic_on: type polymorphic_identity: employee Manager: allOf: - $ref: "#/components/schemas/Employee" - x-inherits: true type: object properties: manager_data: type: string x-kwargs: __mapper_args__: polymorphic_identity: manager |
See also
- Single Table
Full example for single table inheritance.
- SQLAlchemy single table inheritance documentation
Documentation for SQLAlchemy single table inheritance.