mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-python
synced 2026-01-15 09:53:32 +01:00
Add db.schema framework
jw.db.schema is a set of classes meant as an interface to describe a database schema. Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
cc7aeeac31
commit
17ab47e96a
8 changed files with 556 additions and 0 deletions
184
tools/python/jwutils/db/schema/Table.py
Normal file
184
tools/python/jwutils/db/schema/Table.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional, Iterable, List, Any # TODO: Need any for many things, as I can't figure out how to avoid circular imports from here
|
||||
|
||||
import abc
|
||||
from collections import OrderedDict
|
||||
|
||||
from ...log import *
|
||||
|
||||
from .ColumnSet import ColumnSet
|
||||
from .DataType import DataType
|
||||
from .CompositeForeignKey import CompositeForeignKey
|
||||
from .Column import Column
|
||||
|
||||
class Table(abc.ABC): # export
|
||||
|
||||
def __init__(self, schema, name: str):
|
||||
assert(isinstance(name, str))
|
||||
self.__schema = schema
|
||||
self.__name = name
|
||||
self.__primary_keys: Optional[Iterable[str]] = None
|
||||
self.__unique_constraints: Optional[List[ColumnSet]] = None
|
||||
self.__foreign_key_constraints: Optional[List[CompositeForeignKey]] = None
|
||||
self.___columns: Optional[OrderedDict[str, Any]] = None
|
||||
self.__nullable_columns: Optional[Iterable[str]] = None
|
||||
self.__non_nullable_columns: Optional[Iterable[str]] = None
|
||||
self.__null_insertible_columns: Optional[Iterable[str]] = None
|
||||
self.__not_null_insertible_columns: Optional[Iterable[str]] = None
|
||||
self.__log_columns: Optional[Iterable[str]] = None
|
||||
self.__column_default: Optional[dict[str, Any]] = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.__name
|
||||
|
||||
@property
|
||||
def __columns(self):
|
||||
if self.___columns is None:
|
||||
ret: OrderedDict[str, Any] = OrderedDict()
|
||||
for name in self._column_names():
|
||||
ret[name] = Column(self, name, self._column_data_type(name))
|
||||
self.___columns = ret
|
||||
return self.___columns
|
||||
|
||||
# -- To be reimplemented
|
||||
|
||||
@abc.abstractmethod
|
||||
def _column_names(self) -> Iterable[str]:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _log_columns(self) -> Iterable[str]:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _column_data_type(self, name) -> DataType:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _primary_keys(self) -> Iterable[str]:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _auto_increment_columns(self) -> Iterable[str]:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _column_default(self, name) -> Any:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _unique_constraints(self) -> List[List[str]]:
|
||||
pass
|
||||
|
||||
def _model_name(self) -> Optional[str]:
|
||||
slog(WARNING, f'Returning None model name for table {self.name}')
|
||||
return None
|
||||
|
||||
# -- To be used
|
||||
|
||||
def column_default(self, name) -> Any:
|
||||
if self.__column_default is None:
|
||||
ret: dict[str, Any] = dict()
|
||||
for name in self.column_names:
|
||||
ret[name] = self._column_default(name)
|
||||
self.__column_default = ret
|
||||
return self.__column_default[name]
|
||||
|
||||
@property
|
||||
def columns(self):
|
||||
return self.__columns.values()
|
||||
|
||||
def column(self, name):
|
||||
return self.__columns[name]
|
||||
|
||||
@property
|
||||
def column_names(self) -> Iterable[str]:
|
||||
return self.__columns.keys()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
@property
|
||||
def schema(self):
|
||||
return self.__schema
|
||||
|
||||
@property
|
||||
def model_name(self) -> Optional[str]:
|
||||
return self._model_name()
|
||||
|
||||
@property
|
||||
def primary_keys(self) -> Iterable[str]:
|
||||
if self.__primary_keys is None:
|
||||
self.__primary_keys = self._primary_keys()
|
||||
return self.__primary_keys
|
||||
|
||||
@property
|
||||
def nullable_columns(self) -> Iterable[str]:
|
||||
if self.__nullable_columns is None:
|
||||
self.__nullable_columns = self._nullable_columns()
|
||||
return self.__nullable_columns
|
||||
|
||||
@property
|
||||
def non_nullable_columns(self) -> Iterable[str]:
|
||||
if self.__non_nullable_columns is None:
|
||||
ret = []
|
||||
all_cols = self.column_names
|
||||
nullable_columns = self.nullable_columns
|
||||
for col in all_cols:
|
||||
if col not in nullable_columns:
|
||||
ret.append(col)
|
||||
self.__non_nullable_columns = ret
|
||||
return self.__non_nullable_columns
|
||||
|
||||
@property
|
||||
def null_insertible_columns(self) -> Iterable[str]:
|
||||
if self.__null_insertible_columns is None:
|
||||
ret: list[str] = []
|
||||
for col in self.__columns.values():
|
||||
if col.is_null_insertible:
|
||||
ret.append(col.name)
|
||||
self.__null_insertible_columns = ret
|
||||
return self.__null_insertible_columns
|
||||
|
||||
@property
|
||||
def not_null_insertible_columns(self) -> Iterable[str]:
|
||||
if self.__not_null_insertible_columns is None:
|
||||
ret: list[str] = []
|
||||
for col in self.__columns.values():
|
||||
if not col.is_null_insertible:
|
||||
ret.append(col.name)
|
||||
self.__not_null_insertible_columns = ret
|
||||
return self.__not_null_insertible_columns
|
||||
|
||||
@property
|
||||
def log_columns(self):
|
||||
if self.__log_columns is None:
|
||||
self.__log_columns = self._log_columns()
|
||||
return self.__log_columns
|
||||
|
||||
@property
|
||||
def auto_increment_columns(self) -> Iterable[str]:
|
||||
return self._auto_increment_columns()
|
||||
|
||||
@property
|
||||
def unique_constraints(self) -> List[ColumnSet]:
|
||||
if self.__unique_constraints is None:
|
||||
ret: List[ColumnSet] = []
|
||||
impl = self._unique_constraints()
|
||||
if impl is not None:
|
||||
for columns in impl:
|
||||
ret.append(ColumnSet(columns=columns))
|
||||
self.__unique_constraints = ret
|
||||
return self.__unique_constraints
|
||||
|
||||
@property
|
||||
def foreign_key_constraints(self) -> List[CompositeForeignKey]:
|
||||
if self.__foreign_key_constraints is None:
|
||||
ret: List[Any] = []
|
||||
for composite_key in self.__schema.foreign_key_constraints:
|
||||
if composite_key.child_table == self:
|
||||
ret.append(composite_key)
|
||||
self.__foreign_key_constraints = ret
|
||||
return self.__foreign_key_constraints
|
||||
Loading…
Add table
Add a link
Reference in a new issue