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 the x-tablename or x-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 of x-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.