2
2
3
3
from dataclasses import dataclass
4
4
from enum import Enum
5
- from typing import Union , Any , TYPE_CHECKING
5
+ from typing import Any , TYPE_CHECKING
6
6
7
7
from sqlalchemy import ForeignKey , ForeignKeyConstraint
8
- from sqlalchemy .orm import Mapped , mapped_column , relationship
8
+ from sqlalchemy .orm import Mapped , mapped_column , relationship , declared_attr
9
9
10
10
from .db import Base
11
11
from .enums import FieldTypeEnum
12
12
13
13
if TYPE_CHECKING :
14
14
from .models import Entry , Tag , LibraryField
15
15
16
- Field = Union ["TextField" , "TagBoxField" , "DatetimeField" ]
17
16
17
+ class BaseField (Base ):
18
+ __abstract__ = True
18
19
19
- class BooleanField (Base ):
20
- __tablename__ = "boolean_fields"
20
+ @declared_attr
21
+ def id (cls ) -> Mapped [int ]:
22
+ return mapped_column (primary_key = True , autoincrement = True )
21
23
22
- id : Mapped [ int ] = mapped_column ( primary_key = True )
23
- type_key : Mapped [str ] = mapped_column ( ForeignKey ( "library_fields.key" ))
24
- type : Mapped [ LibraryField ] = relationship ( foreign_keys = [ type_key ], lazy = False )
24
+ @ declared_attr
25
+ def type_key ( cls ) -> Mapped [str ]:
26
+ return mapped_column ( ForeignKey ( "library_fields.key" ) )
25
27
26
- entry_id : Mapped [int ] = mapped_column (ForeignKey ("entries.id" ))
27
- entry : Mapped [Entry ] = relationship ()
28
+ @declared_attr
29
+ def type (cls ) -> Mapped [LibraryField ]:
30
+ return relationship (foreign_keys = [cls .type_key ], lazy = False ) # type: ignore
28
31
29
- value : Mapped [bool ]
30
- position : Mapped [int ]
32
+ @declared_attr
33
+ def entry_id (cls ) -> Mapped [int ]:
34
+ return mapped_column (ForeignKey ("entries.id" ))
31
35
32
- def __key (self ):
33
- return (self .type , self .value )
36
+ @declared_attr
37
+ def entry (cls ) -> Mapped [Entry ]:
38
+ return relationship (foreign_keys = [cls .entry_id ]) # type: ignore
39
+
40
+ @declared_attr
41
+ def position (cls ) -> Mapped [int ]:
42
+ return mapped_column ()
34
43
35
44
def __hash__ (self ):
36
45
return hash (self .__key ())
37
46
47
+ def __key (self ):
48
+ raise NotImplementedError
49
+
50
+ value : Any
51
+
52
+
53
+ class BooleanField (BaseField ):
54
+ __tablename__ = "boolean_fields"
55
+
56
+ value : Mapped [bool ]
57
+
58
+ def __key (self ):
59
+ return (self .type , self .value )
60
+
38
61
def __eq__ (self , value ) -> bool :
39
62
if isinstance (value , BooleanField ):
40
63
return self .__key () == value .__key ()
41
64
raise NotImplementedError
42
65
43
66
44
- class TextField (Base ):
67
+ class TextField (BaseField ):
45
68
__tablename__ = "text_fields"
46
69
# constrain for combination of: entry_id, type_key and position
47
70
__table_args__ = (
@@ -51,21 +74,10 @@ class TextField(Base):
51
74
),
52
75
)
53
76
54
- id : Mapped [int ] = mapped_column (primary_key = True , autoincrement = True )
55
- type_key : Mapped [str ] = mapped_column (ForeignKey ("library_fields.key" ))
56
- type : Mapped [LibraryField ] = relationship (foreign_keys = [type_key ], lazy = False )
57
-
58
- entry_id : Mapped [int ] = mapped_column (ForeignKey ("entries.id" ))
59
- entry : Mapped [Entry ] = relationship (foreign_keys = [entry_id ])
60
-
61
77
value : Mapped [str | None ]
62
- position : Mapped [int ]
63
78
64
- def __key (self ):
65
- return (self .type , self .value )
66
-
67
- def __hash__ (self ):
68
- return hash (self .__key ())
79
+ def __key (self ) -> tuple :
80
+ return self .type , self .value
69
81
70
82
def __eq__ (self , value ) -> bool :
71
83
if isinstance (value , TextField ):
@@ -75,18 +87,10 @@ def __eq__(self, value) -> bool:
75
87
raise NotImplementedError
76
88
77
89
78
- class TagBoxField (Base ):
90
+ class TagBoxField (BaseField ):
79
91
__tablename__ = "tag_box_fields"
80
92
81
- id : Mapped [int ] = mapped_column (primary_key = True )
82
- type_key : Mapped [str ] = mapped_column (ForeignKey ("library_fields.key" ))
83
- type : Mapped [LibraryField ] = relationship (foreign_keys = [type_key ], lazy = False )
84
-
85
- entry_id : Mapped [int ] = mapped_column (ForeignKey ("entries.id" ))
86
- entry : Mapped [Entry ] = relationship (foreign_keys = [entry_id ])
87
-
88
93
tags : Mapped [set [Tag ]] = relationship (secondary = "tag_fields" )
89
- position : Mapped [int ]
90
94
91
95
def __key (self ):
92
96
return (
@@ -99,34 +103,20 @@ def value(self) -> None:
99
103
"""For interface compatibility with other field types."""
100
104
return None
101
105
102
- def __hash__ (self ):
103
- return hash (self .__key ())
104
-
105
106
def __eq__ (self , value ) -> bool :
106
107
if isinstance (value , TagBoxField ):
107
108
return self .__key () == value .__key ()
108
109
raise NotImplementedError
109
110
110
111
111
- class DatetimeField (Base ):
112
+ class DatetimeField (BaseField ):
112
113
__tablename__ = "datetime_fields"
113
114
114
- id : Mapped [int ] = mapped_column (primary_key = True )
115
- type_key : Mapped [str ] = mapped_column (ForeignKey ("library_fields.key" ))
116
- type : Mapped [LibraryField ] = relationship (foreign_keys = [type_key ], lazy = False )
117
-
118
- entry_id : Mapped [int ] = mapped_column (ForeignKey ("entries.id" ))
119
- entry : Mapped [Entry ] = relationship (foreign_keys = [entry_id ])
120
-
121
115
value : Mapped [str | None ]
122
- position : Mapped [int ]
123
116
124
117
def __key (self ):
125
118
return (self .type , self .value )
126
119
127
- def __hash__ (self ):
128
- return hash (self .__key ())
129
-
130
120
def __eq__ (self , value ) -> bool :
131
121
if isinstance (value , DatetimeField ):
132
122
return self .__key () == value .__key ()
0 commit comments