-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathtable.py
More file actions
164 lines (141 loc) · 5.35 KB
/
table.py
File metadata and controls
164 lines (141 loc) · 5.35 KB
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from typing import Iterable, Dict
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from typing import Tuple
from typing import Union
from pydbml.exceptions import ColumnNotFoundError
from pydbml.exceptions import IndexNotFoundError
from pydbml.exceptions import UnknownDatabaseError
from .base import SQLObject, DBMLObject
from .column import Column
from .index import Index
from .note import Note
if TYPE_CHECKING: # pragma: no cover
from pydbml.database import Database
from .reference import Reference
class Table(SQLObject, DBMLObject):
'''Class representing table.'''
required_attributes = ('name', 'schema')
dont_compare_fields = ('database',)
def __init__(self,
name: str,
schema: str = 'public',
alias: Optional[str] = None,
columns: Optional[Iterable[Column]] = None,
indexes: Optional[Iterable[Index]] = None,
note: Optional[Union[Note, str]] = None,
header_color: Optional[str] = None,
comment: Optional[str] = None,
abstract: bool = False,
properties: Union[Dict[str, str], None] = None
):
self.database: Optional[Database] = None
self.name = name
self.schema = schema
self._columns: List[Column] = []
for column in columns or []:
self.add_column(column)
self.indexes: List[Index] = []
for index in indexes or []:
self.add_index(index)
self.alias = alias if alias else None
self.note = Note(note)
self.header_color = header_color
self.comment = comment
self.abstract = abstract
self.properties = properties if properties else {}
@property
def columns(self) -> Tuple[Column, ...]:
"""Returns a read-only tuple of columns."""
return tuple(self._columns)
@property
def note(self):
return self._note
@note.setter
def note(self, val: Note) -> None:
self._note = val
val.parent = self
@property
def full_name(self) -> str:
return f'{self.schema}.{self.name}'
def _has_composite_pk(self) -> bool:
return sum(c.pk for c in self.columns) > 1
def add_column(self, c: Column) -> None:
'''
Adds column to self.columns attribute and sets in this column the
`table` attribute.
'''
if not isinstance(c, Column):
raise TypeError('Columns must be of type Column')
c.table = self
self._columns.append(c)
def delete_column(self, c: Union[Column, int]) -> Column:
if isinstance(c, Column):
if c in self._columns:
c.table = None
return self._columns.pop(self._columns.index(c))
else:
raise ColumnNotFoundError(f'Column {c} if missing in the table')
elif isinstance(c, int):
self._columns[c].table = None
return self._columns.pop(c)
def add_index(self, i: Index) -> None:
'''
Adds index to self.indexes attribute and sets in this index the
`table` attribute.
'''
if not isinstance(i, Index):
raise TypeError('Indexes must be of type Index')
for subject in i.subjects:
if isinstance(subject, Column) and subject.table is not self:
raise ColumnNotFoundError(f'Column {subject} not in the table')
i.table = self
self.indexes.append(i)
def delete_index(self, i: Union[Index, int]) -> Index:
if isinstance(i, Index):
if i in self.indexes:
i.table = None
return self.indexes.pop(self.indexes.index(i))
else:
raise IndexNotFoundError(f'Index {i} if missing in the table')
elif isinstance(i, int):
self.indexes[i].table = None
return self.indexes.pop(i)
def get_refs(self) -> List['Reference']:
if not self.database:
raise UnknownDatabaseError('Database for the table is not set')
return [ref for ref in self.database.refs if ref.table1 == self]
def __getitem__(self, k: Union[int, str]) -> Column:
if isinstance(k, int):
return self._columns[k]
elif isinstance(k, str):
for c in self._columns:
if c.name == k:
return c
raise ColumnNotFoundError(f'Column {k} not present in table {self.name}')
else:
raise TypeError('indeces must be str or int')
def get(self, k, default: Optional[Column] = None) -> Optional[Column]:
try:
return self.__getitem__(k)
except (IndexError, ColumnNotFoundError):
return default
def __iter__(self):
return iter(self._columns)
def __repr__(self):
'''
>>> table = Table('customers')
>>> table
<Table 'public' 'customers'>
'''
return f'<Table {self.schema!r} {self.name!r}>'
def __str__(self):
'''
>>> table = Table('customers')
>>> table.add_column(Column('id', 'INTEGER'))
>>> table.add_column(Column('name', 'VARCHAR2'))
>>> print(table)
public.customers(id, name)
'''
return f'{self.schema}.{self.name}({", ".join(c.name for c in self.columns)})'