pyracmon.model API documentation

# Module `pyracmon.model`

Expand source code

    from collections import OrderedDict
    from collections.abc import Iterator, Sequence
    from typing import Any, Union, Optional, Callable, Generic, TypeVar, get_origin, get_args, cast, TYPE_CHECKING
    from typing_extensions import TypeVarTuple, Unpack, Self, dataclass_transform
    from .util import PKS

    #----------------------------------------------------------------
    # Pseudo types used only for type hinting.
    #----------------------------------------------------------------
    M = TypeVar('M')
    MXS = TypeVarTuple('MXS')
    COLUMN = NotImplemented

    class Mixins(Generic[Unpack[MXS]]):
        pass
    MXT = TypeVar('MXT', bound=Mixins)

    if TYPE_CHECKING:
        @dataclass_transform(kw_only_default=True)
        class Meta(type):
            #: Table name.
            name: str
            #: `Table` object.
            table: 'Table'
            #: A list of `Column` s.
            columns: list['Column']
            #: An object exposing `Column` object via the attribute of its name.
            column: Any

            def __iter__(self) -> Iterator[tuple['Column', Any]]: ...
            def __getitem__(self, key: str) -> Any: ...
            def __contains__(self, key) -> bool: ...

            @classmethod
            def shrink(cls, excludes, includes=None) -> Self: ...

        class Model(Mixins[Unpack[MXS]], metaclass=Meta):
            """
            Base type of model types.

            This class only works as a marker of model types and gives no functionalities to them.
            """
            def __init__(self, **kwargs) -> None: ... # for typing
    else:
        class Meta:
            pass
        class Model:
            pass
    #----------------------------------------------------------------
    Record = Union[Meta, dict[str, Any]]
    """Model object or dict corresponding to a table row."""

    class ForeignKey:
        """
        This class represents a foreign key constraint.
        """
        def __init__(self, table: Union['Table', str], column: Union[str, 'Column']) -> None:
            #: Referenced table model, table name is set alternatively when the table is not modelled.
            self.table = table
            #: Referenced column model, column name is set alternatively when the column is not modelled.
            self.column = column

    class Relations:
        """
        This class represents foreign key constraints on a column.
        """
        def __init__(self) -> None:
            #: Foreign key constraints on a column.
            self.constraints: list[ForeignKey] = []

        def add(self, fk: ForeignKey):
            """
            Adds a constraint.

            Args:
                fk: Foreign key constraint.
            """
            self.constraints.append(fk)

    class Column:
        """
        This class represents a schema of a column.
        """
        def __init__(
            self,
            name: str,
            ptype: type,
            type_info: Optional[Any],
            pk: bool,
            fk: Optional[Relations],
            incremental: Optional[Any],
            nullable: bool,
            comment: str = "",
        ):
            #: Column name.
            self.name = name
            #: Data type in python.
            self.ptype = ptype
            #: Type informations obtained from DB.
            self.type_info = type_info
            #: Is this column a primary key?
            self.pk = pk
            #: Foreign key constraints.
            self.fk = fk
            #: If this column is auto-incremental, this object contains the information of the feature, otherwise, `None`.
            self.incremental = incremental
            #: Can this column contain null?
            self.nullable = nullable
            #: Comment of the column.
            self.comment = comment

    class Table:
        """
        This class represents a schema of a table.
        """
        def __init__(self, name: str, columns: list[Column], comment: str = ""):
            #: Table name.
            self.name = name
            #: Columns in the table.
            self.columns = columns
            #: Comment of the table.
            self.comment = comment

        def find(self, name: str) -> Optional[Column]:
            """
            Find a column by name.

            Args:
                name: Column name.
            Returns:
                The column if exists, otherwise `None`.
            """
            return next(filter(lambda c: c.name == name, self.columns), None)

    def define_model(table_: Table, mixins: Union[type[MXT], list[type], None] = None, model_type: Optional[type[M]] = Model) -> type[M]:
        """
        Create a model type representing a table.

        Model type inherits all types in `mixins` in order.
        When the same attribute is defined in multiple mixin types, the former overrides the latter.

        Every model type has following attributes:

        |name|type|description|
        |:---|:---|:---|
        |name|`str`|Name of the table.|
        |table|`Table`|Table schema.|
        |columns|`List[Column]`|List of column schemas.|
        |column|`Any`|An object whose attribute exposes of column schema of its name.|

        Model instances are created by passing the constructor keyword arguments composed of column names and values like builtin dataclass.
        Unlike dataclass, the constructor does not require all of columns.
        Omitted columns don't affect predefined operations such as `CRUDMixin.insert` .
        If `not null` constraint exists on the column, insertion will be denied at runtime and exception will be thrown.

        ```python
        >>> # CREATE TABLE t1 (col1 int, col2 text, col3 text);
        >>> table = define_model("t1")
        >>> model = table(col1=1, col2="a")
        ```

        Attributes are also assignable by normal setter. If attribute name is not a valid column name, `TypeError` raises.

        ```python
        >>> model.col3 = "b"
        ```

        Model instance supports iteration which yields pairs of assigned column schema and its value.

        ```python
        >>> for c, v in model:
        >>>     print(f"{c.name} = {v}")
        col1 = 1
        col2 = a
        col3 = b
        ```

        Args:
            table__: Table schema.
            mixin: Mixin types providing class methods to the model type.
            model_type: Use this just for type hinting to determine returned model type.
        Returns:
            Model type.
        """
        column_names = {c.name for c in table_.columns}

        class Columns:
            def __init__(self):
                for c in table_.columns:
                    setattr(self, c.name, c)

        class Meta(type):
            name = table_.name
            table = table_
            columns = table_.columns
            column = Columns()

            @classmethod
            def shrink(cls, excludes: list[str], includes: Optional[list[str]] = None) -> Self:
                """
                Creates new model type containing subset of columns.

                Args:
                    excludes: Column names to exclude.
                    includes: Column names to include.
                Returns:
                    model type.
                """
                cols = [c for c in cls.columns if (not includes or c.name in includes) and c.name not in excludes]
                return define_model(Table(cls.name, cols, cls.table.comment), mixins) # type: ignore

        class Base(Model, metaclass=Meta):
            pass

        mixin_types: list[type] = []

        if isinstance(mixins, list):
            mixin_types = mixins
        elif get_origin(mixins) is not None:
            mixin_types = cast(list[type], list(get_args(mixins)))
        elif mixins is not None:
            raise ValueError(f"Model mixin types should be specified by Mixins or a list of types.")

        class _Model(type("ModelBase", tuple([Base] + mixin_types), {})):
            def __init__(self, **kwargs):
                for k, v in kwargs.items():
                    setattr(self, k, v)

            def __repr__(self):
                cls = cast(type[Base], type(self))
                return f"{cls.name}({', '.join([f'{c.name}={repr(getattr(self, c.name))}' for c in cls.columns if hasattr(self, c.name)])})"

            def __str__(self):
                cls = cast(type[Base], type(self))
                return f"{cls.name}({', '.join([f'{c.name}={str(getattr(self, c.name))}' for c in cls.columns if hasattr(self, c.name)])})"

            def __iter__(self) -> Iterator[tuple[Column, Any]]:
                cls = cast(type[Base], type(self))
                return map(lambda c: (c, getattr(self, c.name)), filter(lambda c: hasattr(self, c.name), cls.columns))

            def __setattr__(self, key, value):
                cls = cast(type[Base], type(self))
                if key not in column_names:
                    raise TypeError(f"{key} is not a column of {cls.name}")
                object.__setattr__(self, key, value)

            def __getitem__(self, key):
                return getattr(self, key)

            def __contains__(self, key):
                return hasattr(self, key)

            def __eq__(self, other):
                cls = type(self)
                if cls != type(other):
                    return False
                for k in column_names:
                    if hasattr(self, k) ^ hasattr(other, k):
                        return False
                    if getattr(self, k, None) != getattr(other, k, None):
                        return False
                return True

        return cast(type[M], _Model)

    def parse_pks(model: type[Meta], pks: PKS) -> tuple[list[str], list[Any]]:
        """
        Generates a pair of PK columns names and their values from polymorphic input.

        Args:
            model: Model class.
            pks: A dictionary of PK column name and their values or an object of single PK column.
        Returns:
            Names of PK columns and their values.
        """
        if isinstance(pks, dict):
            ordered = check_columns(model, pks, lambda c: c.pk, True)
            return [v[0] for v in ordered], [v[1] for v in ordered]
        else:
            cols = [c.name for c in model.columns if c.pk]
            if len(cols) != 1:
                raise ValueError(f"The number of primary key columns in {model.name} is not 1.")
            return ([cols[0]], [pks])

    def extract_pks(model: type[Meta], record: Record) -> PKS:
        """
        Extract all primary keys from a record.

        Args:
            model: Model class.
            record: A record.
        Returns:
            Primary keys.
        """
        pk_columns = [c.name for c in model.columns if c.pk]
        if isinstance(record, dict):
            pks = dict((c, record[c]) for c in pk_columns if c in record)
        else:
            pks = dict((c, getattr(record, c)) for c in pk_columns if hasattr(record, c))
        if len(pks) != len(pk_columns):
            missing = set(pk_columns) - set(pks.keys())
            raise ValueError(f"Some primary keys are not contained in passed values and auto-increment values: {missing}")
        return pks

    def check_columns(
        model: type[Meta],
        col_map: dict[str, Any],
        condition: Callable[[Column], bool] = lambda c: True,
        requires_all: bool = False,
    ) -> list[tuple[str, Any]]:
        """
        Checks keys of given `dict` match columns selected by a condition from a model.

        Args:
            model: Model class.
            col_map: Dictionary whose keys are column names.
            condition: A function which selects columns from the model.
            requires_all: If `True`, `ValueError` raises when the dictionary does not contain keys of all selected columns.
        """
        names = [c.name for c in model.columns if condition(c)]
        name_set = set(names)
        targets = set(col_map.keys())
        if not name_set >= targets:
            raise ValueError(f"Columns {targets - name_set} are not specified columns of '{model.name}'.")
        if requires_all and not name_set == targets:
            raise ValueError(f"Required columns {name_set - targets} in '{model.name}' are not found.")
        return [(n, col_map[n]) for n in names if n in col_map]

    def model_values(model: type[Meta], values: Record, excludes_pk: bool = False) -> dict[str, Any]:
        """
        Generates a dictionary whose items are pairs of column name and column value.

        Args:
            model: Model class.
            values: Dictionary from column name to column value or a model instance.
            excludes_pk: If `True`, item of PK column is not contained in returned dictionary.
        Returns:
            A dictionary from column name to column value.
        """
        if isinstance(values, (dict, OrderedDict)):
            includes = {c.name for c in model.columns if (not excludes_pk) or (not c.pk)}
            return {k:v for k, v in values.items() if k in includes}
        elif isinstance(values, model):
            return {cv[0].name:cv[1] for cv in values if (not excludes_pk) or (not cv[0].pk)}
        else:
            raise TypeError(f"Required column value is not contained in the dictionary or model.")

## Global variables

`var Record`

  Model object or dict corresponding to a table row.

## Functions

`def check_columns(model: type[Meta], col_map: dict[str, typing.Any], condition: Callable[[Column], bool] = <function <lambda>>, requires_all: bool = False) ‑> list[tuple[str, typing.Any]]`

  Checks keys of given `dict` match columns selected by a condition from a model.

  ## Args

  `model`
    Model class.
  `col_map`
    Dictionary whose keys are column names.
  `condition`
    A function which selects columns from the model.
  `requires_all`
    If `True`, `ValueError` raises when the dictionary does not contain keys of all selected columns.
  Expand source code

      def check_columns(
          model: type[Meta],
          col_map: dict[str, Any],
          condition: Callable[[Column], bool] = lambda c: True,
          requires_all: bool = False,
      ) -> list[tuple[str, Any]]:
          """
          Checks keys of given `dict` match columns selected by a condition from a model.

          Args:
              model: Model class.
              col_map: Dictionary whose keys are column names.
              condition: A function which selects columns from the model.
              requires_all: If `True`, `ValueError` raises when the dictionary does not contain keys of all selected columns.
          """
          names = [c.name for c in model.columns if condition(c)]
          name_set = set(names)
          targets = set(col_map.keys())
          if not name_set >= targets:
              raise ValueError(f"Columns {targets - name_set} are not specified columns of '{model.name}'.")
          if requires_all and not name_set == targets:
              raise ValueError(f"Required columns {name_set - targets} in '{model.name}' are not found.")
          return [(n, col_map[n]) for n in names if n in col_map]

`def define_model(table_: Table, mixins: Union[type[~MXT], list[type], ForwardRef(None)] = None, model_type: Optional[type[~M]] = pyracmon.model.Model) ‑> type[~M]`

  Create a model type representing a table.

  Model type inherits all types in `mixins` in order. When the same attribute is defined in multiple mixin types, the former overrides the latter.

  Every model type has following attributes:

  name | type | description
  --- | --- | ---
  name | `str` | Name of the table.
  table | `Table` | Table schema.
  columns | `List[Column]` | List of column schemas.
  column | `Any` | An object whose attribute exposes of column schema of its name.

  Model instances are created by passing the constructor keyword arguments composed of column names and values like builtin dataclass. Unlike dataclass, the constructor does not require all of columns. Omitted columns don't affect predefined operations such as `CRUDMixin.insert` . If `not null` constraint exists on the column, insertion will be denied at runtime and exception will be thrown.

      >>> # CREATE TABLE t1 (col1 int, col2 text, col3 text);
      >>> table = define_model("t1")
      >>> model = table(col1=1, col2="a")

  Attributes are also assignable by normal setter. If attribute name is not a valid column name, `TypeError` raises.

      >>> model.col3 = "b"

  Model instance supports iteration which yields pairs of assigned column schema and its value.

      >>> for c, v in model:
      >>>     print(f"{c.name} = {v}")
      col1 = 1
      col2 = a
      col3 = b

  ## Args

  `table__`
    Table schema.
  `mixin`
    Mixin types providing class methods to the model type.
  `model_type`
    Use this just for type hinting to determine returned model type.

  ## Returns

  Model type.

  Expand source code

      def define_model(table_: Table, mixins: Union[type[MXT], list[type], None] = None, model_type: Optional[type[M]] = Model) -> type[M]:
          """
          Create a model type representing a table.

          Model type inherits all types in `mixins` in order.
          When the same attribute is defined in multiple mixin types, the former overrides the latter.

          Every model type has following attributes:

          |name|type|description|
          |:---|:---|:---|
          |name|`str`|Name of the table.|
          |table|`Table`|Table schema.|
          |columns|`List[Column]`|List of column schemas.|
          |column|`Any`|An object whose attribute exposes of column schema of its name.|

          Model instances are created by passing the constructor keyword arguments composed of column names and values like builtin dataclass.
          Unlike dataclass, the constructor does not require all of columns.
          Omitted columns don't affect predefined operations such as `CRUDMixin.insert` .
          If `not null` constraint exists on the column, insertion will be denied at runtime and exception will be thrown.

          ```python
          >>> # CREATE TABLE t1 (col1 int, col2 text, col3 text);
          >>> table = define_model("t1")
          >>> model = table(col1=1, col2="a")
          ```

          Attributes are also assignable by normal setter. If attribute name is not a valid column name, `TypeError` raises.

          ```python
          >>> model.col3 = "b"
          ```

          Model instance supports iteration which yields pairs of assigned column schema and its value.

          ```python
          >>> for c, v in model:
          >>>     print(f"{c.name} = {v}")
          col1 = 1
          col2 = a
          col3 = b
          ```

          Args:
              table__: Table schema.
              mixin: Mixin types providing class methods to the model type.
              model_type: Use this just for type hinting to determine returned model type.
          Returns:
              Model type.
          """
          column_names = {c.name for c in table_.columns}

          class Columns:
              def __init__(self):
                  for c in table_.columns:
                      setattr(self, c.name, c)

          class Meta(type):
              name = table_.name
              table = table_
              columns = table_.columns
              column = Columns()

              @classmethod
              def shrink(cls, excludes: list[str], includes: Optional[list[str]] = None) -> Self:
                  """
                  Creates new model type containing subset of columns.

                  Args:
                      excludes: Column names to exclude.
                      includes: Column names to include.
                  Returns:
                      model type.
                  """
                  cols = [c for c in cls.columns if (not includes or c.name in includes) and c.name not in excludes]
                  return define_model(Table(cls.name, cols, cls.table.comment), mixins) # type: ignore

          class Base(Model, metaclass=Meta):
              pass

          mixin_types: list[type] = []

          if isinstance(mixins, list):
              mixin_types = mixins
          elif get_origin(mixins) is not None:
              mixin_types = cast(list[type], list(get_args(mixins)))
          elif mixins is not None:
              raise ValueError(f"Model mixin types should be specified by Mixins or a list of types.")

          class _Model(type("ModelBase", tuple([Base] + mixin_types), {})):
              def __init__(self, **kwargs):
                  for k, v in kwargs.items():
                      setattr(self, k, v)

              def __repr__(self):
                  cls = cast(type[Base], type(self))
                  return f"{cls.name}({', '.join([f'{c.name}={repr(getattr(self, c.name))}' for c in cls.columns if hasattr(self, c.name)])})"

              def __str__(self):
                  cls = cast(type[Base], type(self))
                  return f"{cls.name}({', '.join([f'{c.name}={str(getattr(self, c.name))}' for c in cls.columns if hasattr(self, c.name)])})"

              def __iter__(self) -> Iterator[tuple[Column, Any]]:
                  cls = cast(type[Base], type(self))
                  return map(lambda c: (c, getattr(self, c.name)), filter(lambda c: hasattr(self, c.name), cls.columns))

              def __setattr__(self, key, value):
                  cls = cast(type[Base], type(self))
                  if key not in column_names:
                      raise TypeError(f"{key} is not a column of {cls.name}")
                  object.__setattr__(self, key, value)

              def __getitem__(self, key):
                  return getattr(self, key)

              def __contains__(self, key):
                  return hasattr(self, key)

              def __eq__(self, other):
                  cls = type(self)
                  if cls != type(other):
                      return False
                  for k in column_names:
                      if hasattr(self, k) ^ hasattr(other, k):
                          return False
                      if getattr(self, k, None) != getattr(other, k, None):
                          return False
                  return True

          return cast(type[M], _Model)

`def extract_pks(model: type[Meta], record: Union[Meta, dict[str, Any]]) ‑> Union[Any, dict[str, Any]]`

  Extract all primary keys from a record.

  ## Args

  `model`
    Model class.
  `record`
    A record.

  ## Returns

  Primary keys.

  Expand source code

      def extract_pks(model: type[Meta], record: Record) -> PKS:
          """
          Extract all primary keys from a record.

          Args:
              model: Model class.
              record: A record.
          Returns:
              Primary keys.
          """
          pk_columns = [c.name for c in model.columns if c.pk]
          if isinstance(record, dict):
              pks = dict((c, record[c]) for c in pk_columns if c in record)
          else:
              pks = dict((c, getattr(record, c)) for c in pk_columns if hasattr(record, c))
          if len(pks) != len(pk_columns):
              missing = set(pk_columns) - set(pks.keys())
              raise ValueError(f"Some primary keys are not contained in passed values and auto-increment values: {missing}")
          return pks

`def model_values(model: type[Meta], values: Union[Meta, dict[str, Any]], excludes_pk: bool = False) ‑> dict[str, typing.Any]`

  Generates a dictionary whose items are pairs of column name and column value.

  ## Args

  `model`
    Model class.
  `values`
    Dictionary from column name to column value or a model instance.
  `excludes_pk`
    If `True`, item of PK column is not contained in returned dictionary.

  ## Returns

  A dictionary from column name to column value.

  Expand source code

      def model_values(model: type[Meta], values: Record, excludes_pk: bool = False) -> dict[str, Any]:
          """
          Generates a dictionary whose items are pairs of column name and column value.

          Args:
              model: Model class.
              values: Dictionary from column name to column value or a model instance.
              excludes_pk: If `True`, item of PK column is not contained in returned dictionary.
          Returns:
              A dictionary from column name to column value.
          """
          if isinstance(values, (dict, OrderedDict)):
              includes = {c.name for c in model.columns if (not excludes_pk) or (not c.pk)}
              return {k:v for k, v in values.items() if k in includes}
          elif isinstance(values, model):
              return {cv[0].name:cv[1] for cv in values if (not excludes_pk) or (not cv[0].pk)}
          else:
              raise TypeError(f"Required column value is not contained in the dictionary or model.")

`def parse_pks(model: type[Meta], pks: Union[Any, dict[str, Any]]) ‑> tuple[list[str], list[typing.Any]]`

  Generates a pair of PK columns names and their values from polymorphic input.

  ## Args

  `model`
    Model class.
  `pks`
    A dictionary of PK column name and their values or an object of single PK column.

  ## Returns

  Names of PK columns and their values.

  Expand source code

      def parse_pks(model: type[Meta], pks: PKS) -> tuple[list[str], list[Any]]:
          """
          Generates a pair of PK columns names and their values from polymorphic input.

          Args:
              model: Model class.
              pks: A dictionary of PK column name and their values or an object of single PK column.
          Returns:
              Names of PK columns and their values.
          """
          if isinstance(pks, dict):
              ordered = check_columns(model, pks, lambda c: c.pk, True)
              return [v[0] for v in ordered], [v[1] for v in ordered]
          else:
              cols = [c.name for c in model.columns if c.pk]
              if len(cols) != 1:
                  raise ValueError(f"The number of primary key columns in {model.name} is not 1.")
              return ([cols[0]], [pks])

## Classes

`class Column (name: str, ptype: type, type_info: Optional[Any], pk: bool, fk: Optional[Relations], incremental: Optional[Any], nullable: bool, comment: str = '')`

  This class represents a schema of a column.

  Expand source code

      class Column:
          """
          This class represents a schema of a column.
          """
          def __init__(
              self,
              name: str,
              ptype: type,
              type_info: Optional[Any],
              pk: bool,
              fk: Optional[Relations],
              incremental: Optional[Any],
              nullable: bool,
              comment: str = "",
          ):
              #: Column name.
              self.name = name
              #: Data type in python.
              self.ptype = ptype
              #: Type informations obtained from DB.
              self.type_info = type_info
              #: Is this column a primary key?
              self.pk = pk
              #: Foreign key constraints.
              self.fk = fk
              #: If this column is auto-incremental, this object contains the information of the feature, otherwise, `None`.
              self.incremental = incremental
              #: Can this column contain null?
              self.nullable = nullable
              #: Comment of the column.
              self.comment = comment

  ### Instance variables

  `var comment`

    Comment of the column.

  `var fk`

    Foreign key constraints.

  `var incremental`

    If this column is auto-incremental, this object contains the information of the feature, otherwise, `None`.

  `var name`

    Column name.

  `var nullable`

    Can this column contain null?

  `var pk`

    Is this column a primary key?

  `var ptype`

    Data type in python.

  `var type_info`

    Type informations obtained from DB.

`class ForeignKey (table: Union[ForwardRef('Table'), str], column: Union[str, ForwardRef('Column')])`

  This class represents a foreign key constraint.

  Expand source code

      class ForeignKey:
          """
          This class represents a foreign key constraint.
          """
          def __init__(self, table: Union['Table', str], column: Union[str, 'Column']) -> None:
              #: Referenced table model, table name is set alternatively when the table is not modelled.
              self.table = table
              #: Referenced column model, column name is set alternatively when the column is not modelled.
              self.column = column

  ### Instance variables

  `var column`

    Referenced column model, column name is set alternatively when the column is not modelled.

  `var table`

    Referenced table model, table name is set alternatively when the table is not modelled.

`class Meta`
  Expand source code

      @dataclass_transform(kw_only_default=True)
      class Meta(type):
          #: Table name.
          name: str
          #: `Table` object.
          table: 'Table'
          #: A list of `Column` s.
          columns: list['Column']
          #: An object exposing `Column` object via the attribute of its name.
          column: Any

          def __iter__(self) -> Iterator[tuple['Column', Any]]: ...
          def __getitem__(self, key: str) -> Any: ...
          def __contains__(self, key) -> bool: ...

          @classmethod
          def shrink(cls, excludes, includes=None) -> Self: ...

  ### Subclasses

  - GraphEntityMixin
`class Mixins`

  Abstract base class for generic types.

  A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::

  class Mapping(Generic[KT, VT]): def getitem(self, key: KT) -> VT: … # Etc.

  This class can then be used as follows::

  def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default

  Expand source code

      class Mixins(Generic[Unpack[MXS]]):
          pass

  ### Ancestors

  - typing.Generic
`class Model`
  Expand source code

      class Model(Mixins[Unpack[MXS]], metaclass=Meta):
          """
          Base type of model types.

          This class only works as a marker of model types and gives no functionalities to them.
          """
          def __init__(self, **kwargs) -> None: ... # for typing

`class Relations`

  This class represents foreign key constraints on a column.

  Expand source code

      class Relations:
          """
          This class represents foreign key constraints on a column.
          """
          def __init__(self) -> None:
              #: Foreign key constraints on a column.
              self.constraints: list[ForeignKey] = []

          def add(self, fk: ForeignKey):
              """
              Adds a constraint.

              Args:
                  fk: Foreign key constraint.
              """
              self.constraints.append(fk)

  ### Instance variables

  `var constraints`

    Foreign key constraints on a column.

  ### Methods

  `def add(self, fk: ForeignKey)`

    Adds a constraint.

    ## Args

    `fk`
      Foreign key constraint.
    Expand source code

        def add(self, fk: ForeignKey):
            """
            Adds a constraint.

            Args:
                fk: Foreign key constraint.
            """
            self.constraints.append(fk)

`class Table (name: str, columns: list[Column], comment: str = '')`

  This class represents a schema of a table.

  Expand source code

      class Table:
          """
          This class represents a schema of a table.
          """
          def __init__(self, name: str, columns: list[Column], comment: str = ""):
              #: Table name.
              self.name = name
              #: Columns in the table.
              self.columns = columns
              #: Comment of the table.
              self.comment = comment

          def find(self, name: str) -> Optional[Column]:
              """
              Find a column by name.

              Args:
                  name: Column name.
              Returns:
                  The column if exists, otherwise `None`.
              """
              return next(filter(lambda c: c.name == name, self.columns), None)

  ### Instance variables

  `var columns`

    Columns in the table.

  `var comment`

    Comment of the table.

  `var name`

    Table name.

  ### Methods

  `def find(self, name: str) ‑> Optional[Column]`

    Find a column by name.

    ## Args

    `name`
      Column name.

    ## Returns

    The column if exists, otherwise `None`.

    Expand source code

        def find(self, name: str) -> Optional[Column]:
            """
            Find a column by name.

            Args:
                name: Column name.
            Returns:
                The column if exists, otherwise `None`.
            """
            return next(filter(lambda c: c.name == name, self.columns), None)