python-general-be-lib 0.5.0__tar.gz → 0.5.1__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.
- {python_general_be_lib-0.5.0/python_general_be_lib.egg-info → python_general_be_lib-0.5.1}/PKG-INFO +1 -1
- python_general_be_lib-0.5.1/general/interface/base/base_model.py +64 -0
- python_general_be_lib-0.5.1/general/interface/base/declarative_base.py +144 -0
- python_general_be_lib-0.5.1/general/interface/repository/crud_repository.py +607 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/repository/geometry_repository.py +114 -2
- python_general_be_lib-0.5.1/general/interface/repository/handler/base_handler.py +68 -0
- python_general_be_lib-0.5.1/general/interface/repository/handler/ilike_handler.py +90 -0
- python_general_be_lib-0.5.1/general/interface/repository/handler/interval_handler.py +87 -0
- python_general_be_lib-0.5.1/general/interface/repository/handler/json_handler.py +71 -0
- python_general_be_lib-0.5.1/general/interface/repository/handler/statement_manager.py +103 -0
- python_general_be_lib-0.5.1/general/interface/repository/many_to_many_repository.py +184 -0
- python_general_be_lib-0.5.1/general/interface/repository/view_repository.py +120 -0
- python_general_be_lib-0.5.1/general/logger.py +135 -0
- python_general_be_lib-0.5.1/general/paginator_dto.py +74 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/pyproject.toml +1 -1
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1/python_general_be_lib.egg-info}/PKG-INFO +1 -1
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/setup.py +1 -1
- python_general_be_lib-0.5.0/general/interface/base/base_model.py +0 -27
- python_general_be_lib-0.5.0/general/interface/base/declarative_base.py +0 -57
- python_general_be_lib-0.5.0/general/interface/repository/crud_repository.py +0 -276
- python_general_be_lib-0.5.0/general/interface/repository/handler/base_handler.py +0 -23
- python_general_be_lib-0.5.0/general/interface/repository/handler/ilike_handler.py +0 -33
- python_general_be_lib-0.5.0/general/interface/repository/handler/interval_handler.py +0 -33
- python_general_be_lib-0.5.0/general/interface/repository/handler/json_handler.py +0 -29
- python_general_be_lib-0.5.0/general/interface/repository/handler/statement_manager.py +0 -40
- python_general_be_lib-0.5.0/general/interface/repository/many_to_many_repository.py +0 -101
- python_general_be_lib-0.5.0/general/interface/repository/view_repository.py +0 -57
- python_general_be_lib-0.5.0/general/logger.py +0 -76
- python_general_be_lib-0.5.0/general/paginator_dto.py +0 -23
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/LICENSE +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/README.md +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/exception/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/exception/access_exceptions.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/exception/crud_exceptions.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/exception/exception_interface.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/base/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/metadata/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/metadata/crud_metadata.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/metadata/geom_metadata.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/repository/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/general/interface/repository/handler/__init__.py +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/python_general_be_lib.egg-info/SOURCES.txt +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/python_general_be_lib.egg-info/dependency_links.txt +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/python_general_be_lib.egg-info/requires.txt +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/python_general_be_lib.egg-info/top_level.txt +0 -0
- {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.1}/setup.cfg +0 -0
{python_general_be_lib-0.5.0/python_general_be_lib.egg-info → python_general_be_lib-0.5.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-general-be-lib
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities.
|
|
5
5
|
Author-email: Andrea Di Placido <a.diplacido@arpes.it>, "Arpes S.r.l." <it.admin@arpes.it>
|
|
6
6
|
License: MIT
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
__all__ = ["ExtBaseModel", "CamelExtBaseModel"]
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
from pydantic.alias_generators import to_camel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConfigModel(BaseModel):
|
|
8
|
+
"""Modello Pydantic base con configurazione condivisa.
|
|
9
|
+
|
|
10
|
+
Abilita:
|
|
11
|
+
- ``from_attributes=True``: validazione da oggetti ORM SQLAlchemy.
|
|
12
|
+
- ``populate_by_name=True``: accetta sia il nome Python che l'alias.
|
|
13
|
+
- ``arbitrary_types_allowed=True``: consente tipi non standard (es. ``WKBElement``).
|
|
14
|
+
- ``validate_default=True``: valida i valori di default alla creazione.
|
|
15
|
+
"""
|
|
16
|
+
model_config = ConfigDict(from_attributes=True, populate_by_name=True, arbitrary_types_allowed=True, validate_default=True)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CamelConfigModel(ConfigModel):
|
|
20
|
+
"""Estende ``ConfigModel`` aggiungendo la generazione automatica di alias camelCase."""
|
|
21
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def get_attr_by_alias(cls, alias: str) -> str:
|
|
25
|
+
"""Restituisce il nome del campo Python dato un alias camelCase.
|
|
26
|
+
|
|
27
|
+
Utile per risalire al nome interno del campo quando si riceve
|
|
28
|
+
un payload con chiavi in camelCase.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
alias (str): Alias camelCase del campo (es. ``"myField"``).
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: Nome Python del campo (es. ``"my_field"``),
|
|
35
|
+
oppure l'alias stesso se non trovato.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> class MyModel(CamelExtBaseModel):
|
|
39
|
+
... my_field: str
|
|
40
|
+
>>> MyModel.get_attr_by_alias("myField")
|
|
41
|
+
'my_field'
|
|
42
|
+
"""
|
|
43
|
+
for name, field in cls.model_fields.items():
|
|
44
|
+
if field.alias == alias:
|
|
45
|
+
return name
|
|
46
|
+
return alias
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ExtBaseModel(ConfigModel):
|
|
50
|
+
"""Modello Pydantic con configurazione standard per l'integrazione ORM.
|
|
51
|
+
|
|
52
|
+
Eredita da ``ConfigModel``. Da usare come base per tutti i modelli
|
|
53
|
+
che devono essere validati a partire da entità SQLAlchemy.
|
|
54
|
+
"""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class CamelExtBaseModel(CamelConfigModel):
|
|
59
|
+
"""Modello Pydantic con alias camelCase automatici e integrazione ORM.
|
|
60
|
+
|
|
61
|
+
Eredita da ``CamelConfigModel``. Da usare quando l'API espone
|
|
62
|
+
campi in camelCase ma internamente si lavora con snake_case.
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
__all__ = ["Base", "retrieve_mapper_entity", "retrieve_mapper_from_table"]
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Any
|
|
4
|
+
|
|
5
|
+
from geoalchemy2 import Geometry, WKBElement
|
|
6
|
+
from pydantic import create_model
|
|
7
|
+
from sqlalchemy.inspection import inspect
|
|
8
|
+
from sqlalchemy.orm import Mapper, as_declarative
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@as_declarative()
|
|
12
|
+
class Base:
|
|
13
|
+
"""Classe base dichiarativa SQLAlchemy arricchita con metodi di utilità.
|
|
14
|
+
|
|
15
|
+
Tutte le entità ORM del progetto devono estendere questa classe.
|
|
16
|
+
Fornisce accesso rapido a colonne, chiavi primarie e conversione verso Pydantic.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def to_pydantic(cls, model_name: str = None, full_optional: bool = False, default_values: dict[str, Any] = None, **create_model_kwargs):
|
|
21
|
+
"""Genera dinamicamente un modello Pydantic a partire dalle colonne dell'entità.
|
|
22
|
+
|
|
23
|
+
Itera sulle colonne mappate dall'ORM e costruisce i campi del modello
|
|
24
|
+
rispettando la nullabilità e i valori di default forniti.
|
|
25
|
+
Le colonne di tipo ``Geometry`` vengono mappate su ``WKBElement``.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
model_name (str, optional): Nome del modello generato.
|
|
29
|
+
Se non fornito, viene usato ``{ClassName}Model``.
|
|
30
|
+
full_optional (bool): Se ``True``, tutti i campi diventano ``Optional``
|
|
31
|
+
con default ``None``, indipendentemente dalla nullabilità nel DB.
|
|
32
|
+
Default: ``False``.
|
|
33
|
+
default_values (dict[str, Any], optional): Dizionario
|
|
34
|
+
``{nome_campo: valore_default}`` per sovrascrivere i default inferiti.
|
|
35
|
+
**create_model_kwargs: Argomenti aggiuntivi passati a ``pydantic.create_model``.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Type[BaseModel]: Classe Pydantic generata dinamicamente.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> UserModel = User.to_pydantic(full_optional=True)
|
|
42
|
+
>>> PatchModel = User.to_pydantic(model_name="UserPatch", full_optional=True)
|
|
43
|
+
"""
|
|
44
|
+
if not default_values:
|
|
45
|
+
default_values = dict()
|
|
46
|
+
mapper: Mapper = inspect(cls)
|
|
47
|
+
model_name = f"{cls.__name__}Model" if not model_name else model_name
|
|
48
|
+
fields = dict()
|
|
49
|
+
field_values = {col.name: None if full_optional else ... for col in mapper.columns}
|
|
50
|
+
field_values.update(default_values)
|
|
51
|
+
for col in mapper.columns:
|
|
52
|
+
field_name = col.name
|
|
53
|
+
if isinstance(col.type, Geometry):
|
|
54
|
+
field_type = Optional[WKBElement] if col.nullable or full_optional else WKBElement
|
|
55
|
+
else:
|
|
56
|
+
field_type = Optional[col.type.python_type] if col.nullable or full_optional else col.type.python_type
|
|
57
|
+
fields[field_name] = (field_type, field_values[field_name])
|
|
58
|
+
return create_model(model_name, **create_model_kwargs, **fields)
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def columns(cls):
|
|
62
|
+
"""Restituisce la collezione di colonne della tabella associata all'entità.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
ReadOnlyColumnCollection: Collezione delle colonne SQLAlchemy.
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> User.columns()
|
|
69
|
+
"""
|
|
70
|
+
return cls.__table__.c
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def columns_names(cls):
|
|
74
|
+
"""Restituisce i nomi di tutte le colonne della tabella.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
list[str]: Lista dei nomi delle colonne nell'ordine di definizione.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
>>> User.columns_names()
|
|
81
|
+
['id', 'name', 'email']
|
|
82
|
+
"""
|
|
83
|
+
return [col.name for col in cls.__table__.c]
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def primary_key(cls):
|
|
87
|
+
"""Restituisce le colonne che compongono la chiave primaria della tabella.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
list[Column]: Lista delle colonne che formano la primary key.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> User.primary_key()
|
|
94
|
+
[Column('id', Integer(), ...)]
|
|
95
|
+
"""
|
|
96
|
+
return cls.__table__.primary_key.columns.values()
|
|
97
|
+
|
|
98
|
+
def to_dict(self):
|
|
99
|
+
"""Converte l'istanza in un dizionario ``{colonna: valore}``.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
dict[str, Any]: Dizionario con i valori correnti dell'istanza.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
>>> user.to_dict()
|
|
106
|
+
{'id': 1, 'name': 'Mario', 'email': 'mario@example.com'}
|
|
107
|
+
"""
|
|
108
|
+
return {key: getattr(self, key) for key in self.__class__.columns_names()}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def retrieve_mapper_entity(entity_name: str):
|
|
112
|
+
"""Cerca e restituisce una sottoclasse di ``Base`` per nome della classe.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
entity_name (str): Nome della classe ORM da cercare (es. ``"User"``).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Type[Base] | None: La classe ORM corrispondente, o ``None`` se non trovata.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> retrieve_mapper_entity("User").__tablename__
|
|
122
|
+
'users'
|
|
123
|
+
"""
|
|
124
|
+
for mapper in Base.__subclasses__():
|
|
125
|
+
if mapper.__name__ == entity_name:
|
|
126
|
+
return mapper
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def retrieve_mapper_from_table(table_name: str):
|
|
130
|
+
"""Cerca e restituisce una sottoclasse di ``Base`` per nome della tabella.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
table_name (str): Nome della tabella nel database (es. ``"users"``).
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Type[Base] | None: La classe ORM corrispondente, o ``None`` se non trovata.
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
>>> retrieve_mapper_from_table("users").__name__
|
|
140
|
+
'User'
|
|
141
|
+
"""
|
|
142
|
+
for mapper in Base.__subclasses__():
|
|
143
|
+
if mapper.__tablename__ == table_name:
|
|
144
|
+
return mapper
|