architectonics 0.0.25__tar.gz → 0.0.26__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {architectonics-0.0.25 → architectonics-0.0.26}/PKG-INFO +1 -1
- architectonics-0.0.26/architectonics/core/models/base_model.py +9 -0
- architectonics-0.0.26/architectonics/infrastructure/config/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/infrastructure/entities/base_entity.py +4 -0
- architectonics-0.0.26/architectonics/infrastructure/repositories/__init__.py +0 -0
- architectonics-0.0.26/architectonics/infrastructure/repositories/base_exceptions.py +10 -0
- architectonics-0.0.26/architectonics/infrastructure/repositories/base_repository.py +149 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/pyproject.toml +1 -1
- {architectonics-0.0.25 → architectonics-0.0.26}/setup.py +4 -2
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/common/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/common/utils/utils.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/config/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/config/application_settings.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/factory/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/factory/factory.py +0 -0
- {architectonics-0.0.25/architectonics/infrastructure → architectonics-0.0.26/architectonics/core/models}/__init__.py +0 -0
- {architectonics-0.0.25/architectonics/infrastructure/config → architectonics-0.0.26/architectonics/infrastructure}/__init__.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/infrastructure/config/database.py +0 -0
- {architectonics-0.0.25 → architectonics-0.0.26}/architectonics/infrastructure/config/database_settings.py +0 -0
|
File without changes
|
|
@@ -4,6 +4,7 @@ from sqlalchemy import Column, DateTime, String
|
|
|
4
4
|
from sqlalchemy.orm import as_declarative
|
|
5
5
|
|
|
6
6
|
from architectonics.common.utils.utils import get_current_datetime
|
|
7
|
+
from architectonics.core.models.base_model import BaseModel
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@as_declarative()
|
|
@@ -28,3 +29,6 @@ class BaseEntity:
|
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
PK_FIELD = "id"
|
|
32
|
+
|
|
33
|
+
def to_model(self) -> BaseModel:
|
|
34
|
+
raise NotImplementedError()
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class ObjectNotFoundException(Exception):
|
|
2
|
+
"""Exception raised when can't find a row."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ObjectAlreadyExistsException(Exception):
|
|
6
|
+
"""Exception raised when unique constraint failes."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IntegrityErrorException(Exception):
|
|
10
|
+
"""Exception raised when integrity constraint fails."""
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
from asyncpg.exceptions import ForeignKeyViolationError
|
|
5
|
+
from sqlalchemy import delete, select, update
|
|
6
|
+
from sqlalchemy.exc import IntegrityError
|
|
7
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
|
+
|
|
9
|
+
from architectonics.core.models.base_model import BaseModel
|
|
10
|
+
from infrastructure.entities.base_entity import BaseEntity
|
|
11
|
+
from infrastructure.repositories.base_exceptions import (
|
|
12
|
+
IntegrityErrorException,
|
|
13
|
+
ObjectAlreadyExistsException,
|
|
14
|
+
ObjectNotFoundException,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AbstractBaseRepository(ABC):
|
|
19
|
+
_entity: type[BaseEntity] = NotImplemented
|
|
20
|
+
_session: Callable = NotImplemented
|
|
21
|
+
_integrity_error: type[IntegrityError] = IntegrityError
|
|
22
|
+
|
|
23
|
+
def get_session(self) -> AsyncSession:
|
|
24
|
+
return self._session()
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def model_fields(self):
|
|
28
|
+
return self._entity.__table__.columns
|
|
29
|
+
|
|
30
|
+
async def create_model(
|
|
31
|
+
self,
|
|
32
|
+
values: dict[str, any],
|
|
33
|
+
) -> BaseModel:
|
|
34
|
+
|
|
35
|
+
entity = self._entity(**values)
|
|
36
|
+
|
|
37
|
+
async with self.get_session() as session:
|
|
38
|
+
session.add(entity)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
await session.commit()
|
|
42
|
+
except self._integrity_error as e:
|
|
43
|
+
raise ObjectAlreadyExistsException(e)
|
|
44
|
+
|
|
45
|
+
return entity.to_model()
|
|
46
|
+
|
|
47
|
+
async def get_model_by_id(
|
|
48
|
+
self,
|
|
49
|
+
model_id: str,
|
|
50
|
+
) -> BaseModel:
|
|
51
|
+
|
|
52
|
+
statement = select(
|
|
53
|
+
self._entity,
|
|
54
|
+
).where(
|
|
55
|
+
self._entity.id == model_id,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async with self.get_session() as session:
|
|
59
|
+
|
|
60
|
+
result = await session.execute(
|
|
61
|
+
statement=statement,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
entity = result.scalars().first()
|
|
65
|
+
|
|
66
|
+
if entity is None:
|
|
67
|
+
raise ObjectNotFoundException()
|
|
68
|
+
|
|
69
|
+
return entity.to_model()
|
|
70
|
+
|
|
71
|
+
async def update_model(
|
|
72
|
+
self,
|
|
73
|
+
model_id: str,
|
|
74
|
+
values: dict[str, any],
|
|
75
|
+
) -> BaseModel:
|
|
76
|
+
|
|
77
|
+
filtered_values = {k: v for k, v in values.items() if v is not None}
|
|
78
|
+
|
|
79
|
+
update_statement = (
|
|
80
|
+
update(
|
|
81
|
+
self._entity,
|
|
82
|
+
)
|
|
83
|
+
.where(
|
|
84
|
+
self._entity.id == model_id,
|
|
85
|
+
)
|
|
86
|
+
.values(
|
|
87
|
+
**filtered_values,
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
get_statement = select(
|
|
92
|
+
self._entity,
|
|
93
|
+
).where(
|
|
94
|
+
self._entity.id == model_id,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
async with self.get_session() as session:
|
|
98
|
+
try:
|
|
99
|
+
result = await session.execute(update_statement)
|
|
100
|
+
|
|
101
|
+
await session.commit()
|
|
102
|
+
except self._integrity_error as e:
|
|
103
|
+
orig = getattr(e.orig, "__cause__", None)
|
|
104
|
+
|
|
105
|
+
if isinstance(orig, ForeignKeyViolationError):
|
|
106
|
+
raise ObjectNotFoundException()
|
|
107
|
+
|
|
108
|
+
raise IntegrityErrorException(e)
|
|
109
|
+
|
|
110
|
+
result = await session.execute(get_statement)
|
|
111
|
+
model = result.scalars().first()
|
|
112
|
+
|
|
113
|
+
if model is None:
|
|
114
|
+
raise ObjectNotFoundException()
|
|
115
|
+
|
|
116
|
+
return model.to_model()
|
|
117
|
+
|
|
118
|
+
async def delete_model(
|
|
119
|
+
self,
|
|
120
|
+
model_id: str,
|
|
121
|
+
) -> None:
|
|
122
|
+
|
|
123
|
+
statement = delete(
|
|
124
|
+
self._entity,
|
|
125
|
+
).where(
|
|
126
|
+
self._entity.id == model_id,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
async with self.get_session() as session:
|
|
130
|
+
await session.execute(statement)
|
|
131
|
+
await session.commit()
|
|
132
|
+
|
|
133
|
+
async def get_models_list(
|
|
134
|
+
self,
|
|
135
|
+
) -> list[BaseModel]:
|
|
136
|
+
|
|
137
|
+
statement = select(
|
|
138
|
+
self._entity,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
async with self.get_session() as session:
|
|
142
|
+
|
|
143
|
+
result = await session.execute(
|
|
144
|
+
statement=statement,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
models = result.scalars().all()
|
|
148
|
+
|
|
149
|
+
return [model.to_model() for model in models]
|
|
@@ -8,9 +8,11 @@ packages = \
|
|
|
8
8
|
'architectonics.core',
|
|
9
9
|
'architectonics.core.config',
|
|
10
10
|
'architectonics.core.factory',
|
|
11
|
+
'architectonics.core.models',
|
|
11
12
|
'architectonics.infrastructure',
|
|
12
13
|
'architectonics.infrastructure.config',
|
|
13
|
-
'architectonics.infrastructure.entities'
|
|
14
|
+
'architectonics.infrastructure.entities',
|
|
15
|
+
'architectonics.infrastructure.repositories']
|
|
14
16
|
|
|
15
17
|
package_data = \
|
|
16
18
|
{'': ['*']}
|
|
@@ -27,7 +29,7 @@ install_requires = \
|
|
|
27
29
|
|
|
28
30
|
setup_kwargs = {
|
|
29
31
|
'name': 'architectonics',
|
|
30
|
-
'version': '0.0.
|
|
32
|
+
'version': '0.0.26',
|
|
31
33
|
'description': '',
|
|
32
34
|
'long_description': None,
|
|
33
35
|
'author': 'Your Name',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{architectonics-0.0.25 → architectonics-0.0.26}/architectonics/core/config/application_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{architectonics-0.0.25 → architectonics-0.0.26}/architectonics/infrastructure/config/database.py
RENAMED
|
File without changes
|
|
File without changes
|