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 inheritance is supported, model inheritance is planned for the future.

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
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"

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
4
5
from open_alchemy import init_yaml

init_yaml(
    "all-of-column-example-spec.yml", models_filename="all_of_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
4
5
from open_alchemy import init_yaml

init_yaml(
    "all-of-model-example-spec.yml", models_filename="all_of_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.