sqlalchemy-serializer-meta 0.2.0__tar.gz → 1.0.0__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.
- {sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta.egg-info → sqlalchemy_serializer_meta-1.0.0}/PKG-INFO +28 -13
- {sqlalchemy_serializer_meta-0.2.0 → sqlalchemy_serializer_meta-1.0.0}/README.md +2 -2
- sqlalchemy_serializer_meta-1.0.0/pyproject.toml +66 -0
- sqlalchemy_serializer_meta-1.0.0/setup.cfg +4 -0
- sqlalchemy_serializer_meta-1.0.0/setup.py +4 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/__init__.py +52 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/compat.py +14 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/exceptions.py +77 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/fields.py +62 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/serializer.py +703 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/transformers.py +62 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/validator.py +27 -0
- {sqlalchemy_serializer_meta-0.2.0 → sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info}/PKG-INFO +28 -13
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info/SOURCES.txt +20 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info/requires.txt +13 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_flask_integration.py +94 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_model_serializer.py +238 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_serializers.py +117 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_transformers_and_validators.py +96 -0
- sqlalchemy_serializer_meta-0.2.0/setup.cfg +0 -7
- sqlalchemy_serializer_meta-0.2.0/setup.py +0 -26
- sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta/__init__.py +0 -426
- sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta/serializer.py +0 -199
- sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta/validator.py +0 -20
- sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta.egg-info/SOURCES.txt +0 -12
- sqlalchemy_serializer_meta-0.2.0/sqlalchemy_serializer_meta.egg-info/requires.txt +0 -3
- {sqlalchemy_serializer_meta-0.2.0 → sqlalchemy_serializer_meta-1.0.0}/LICENSE +0 -0
- {sqlalchemy_serializer_meta-0.2.0 → sqlalchemy_serializer_meta-1.0.0}/sqlalchemy_serializer_meta.egg-info/dependency_links.txt +0 -0
- {sqlalchemy_serializer_meta-0.2.0 → sqlalchemy_serializer_meta-1.0.0}/sqlalchemy_serializer_meta.egg-info/top_level.txt +0 -0
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy_serializer_meta
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
5
|
-
|
|
6
|
-
Author: Waheed Khaled Elhariri
|
|
7
|
-
Author-email: waheedkhaled95@gmail.com
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: High-performance DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation
|
|
5
|
+
Author-email: Waheed Khaled Elhariri <waheedkhaled95@gmail.com>
|
|
8
6
|
License: MIT
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Project-URL: Homepage, https://github.com/Wehda23/sqlalchemy_serializer_meta
|
|
8
|
+
Project-URL: Repository, https://github.com/Wehda23/sqlalchemy_serializer_meta
|
|
9
|
+
Project-URL: Issues, https://github.com/Wehda23/sqlalchemy_serializer_meta/issues
|
|
10
|
+
Keywords: sqlalchemy,serializer,pydantic,flask,drf,rest,orm
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Framework :: Flask
|
|
23
|
+
Requires-Python: >=3.9
|
|
17
24
|
Description-Content-Type: text/markdown
|
|
18
25
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: sqlalchemy
|
|
20
|
-
Requires-Dist: pydantic
|
|
21
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: sqlalchemy>=1.4.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: typing_extensions>=4.6.0; python_version < "3.11"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
34
|
+
Requires-Dist: Flask>=2.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: Flask-SQLAlchemy>=3.0.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
22
37
|
|
|
23
38
|
# Serializer Module for Flask Application
|
|
24
39
|
|
|
25
40
|
This module provides a set of serializers for your Flask application, allowing you to easily validate and serialize data. The serializers are built with flexibility and extendability in mind, providing features such as nested serialization and integration with Pydantic models.
|
|
26
41
|
|
|
27
|
-
##
|
|
42
|
+
## Table of Contents
|
|
28
43
|
1. [Installation](#installation)
|
|
29
44
|
2. [Basic Usage](#usage)
|
|
30
45
|
3. [Creating Custom Serializers](#custom-serializers)
|
|
@@ -36,7 +51,7 @@ This module provides a set of serializers for your Flask application, allowing y
|
|
|
36
51
|
To use the serializers in your project, simply include the necessary files in your application directory.
|
|
37
52
|
|
|
38
53
|
```bash
|
|
39
|
-
pip install sqlalchemy-serializer-meta
|
|
54
|
+
pip install sqlalchemy-serializer-meta
|
|
40
55
|
```
|
|
41
56
|
|
|
42
57
|
## Usage
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This module provides a set of serializers for your Flask application, allowing you to easily validate and serialize data. The serializers are built with flexibility and extendability in mind, providing features such as nested serialization and integration with Pydantic models.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Table of Contents
|
|
6
6
|
1. [Installation](#installation)
|
|
7
7
|
2. [Basic Usage](#usage)
|
|
8
8
|
3. [Creating Custom Serializers](#custom-serializers)
|
|
@@ -14,7 +14,7 @@ This module provides a set of serializers for your Flask application, allowing y
|
|
|
14
14
|
To use the serializers in your project, simply include the necessary files in your application directory.
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
pip install sqlalchemy-serializer-meta
|
|
17
|
+
pip install sqlalchemy-serializer-meta
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sqlalchemy_serializer_meta"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "High-performance DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Waheed Khaled Elhariri", email = "waheedkhaled95@gmail.com" }
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
keywords = ["sqlalchemy", "serializer", "pydantic", "flask", "drf", "rest", "orm"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Framework :: Flask",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"sqlalchemy>=1.4.0",
|
|
32
|
+
"pydantic>=2.0.0",
|
|
33
|
+
"typing_extensions>=4.6.0; python_version < '3.11'",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.4.0",
|
|
39
|
+
"pytest-cov>=4.1.0",
|
|
40
|
+
"ruff>=0.4.0",
|
|
41
|
+
"mypy>=1.10.0",
|
|
42
|
+
"Flask>=2.0.0",
|
|
43
|
+
"Flask-SQLAlchemy>=3.0.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/Wehda23/sqlalchemy_serializer_meta"
|
|
48
|
+
Repository = "https://github.com/Wehda23/sqlalchemy_serializer_meta"
|
|
49
|
+
Issues = "https://github.com/Wehda23/sqlalchemy_serializer_meta/issues"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["."]
|
|
53
|
+
include = ["sqlalchemy_serializer_meta*"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
minversion = "7.0"
|
|
57
|
+
addopts = "-ra -q --strict-markers"
|
|
58
|
+
testpaths = ["tests"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = "py39"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "W", "I"]
|
|
66
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
sqlalchemy_serializer_meta
|
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
6
|
+
|
|
7
|
+
A fast, flexible DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .compat import Self
|
|
11
|
+
from .exceptions import SerializerError, ValidationError
|
|
12
|
+
from .fields import Field, SerializerMethodField
|
|
13
|
+
from .serializer import (
|
|
14
|
+
AbstractSerializer,
|
|
15
|
+
Creatable,
|
|
16
|
+
Deletable,
|
|
17
|
+
ExtendsPydantic,
|
|
18
|
+
ModelSerializer,
|
|
19
|
+
Representable,
|
|
20
|
+
Saveable,
|
|
21
|
+
Serializer,
|
|
22
|
+
SerializerInterface,
|
|
23
|
+
ToInstance,
|
|
24
|
+
Updatable,
|
|
25
|
+
Validatable,
|
|
26
|
+
)
|
|
27
|
+
from .transformers import to_jsonable_python
|
|
28
|
+
from .validator import BaseValidator
|
|
29
|
+
|
|
30
|
+
__version__ = "1.0.0"
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"AbstractSerializer",
|
|
34
|
+
"BaseValidator",
|
|
35
|
+
"Creatable",
|
|
36
|
+
"Deletable",
|
|
37
|
+
"ExtendsPydantic",
|
|
38
|
+
"Field",
|
|
39
|
+
"ModelSerializer",
|
|
40
|
+
"Representable",
|
|
41
|
+
"Saveable",
|
|
42
|
+
"Self",
|
|
43
|
+
"Serializer",
|
|
44
|
+
"SerializerError",
|
|
45
|
+
"SerializerInterface",
|
|
46
|
+
"SerializerMethodField",
|
|
47
|
+
"ToInstance",
|
|
48
|
+
"Updatable",
|
|
49
|
+
"Validatable",
|
|
50
|
+
"ValidationError",
|
|
51
|
+
"to_jsonable_python",
|
|
52
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Compatibility helpers across Python and dependency versions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if sys.version_info >= (3, 11):
|
|
10
|
+
from typing import Self
|
|
11
|
+
else:
|
|
12
|
+
from typing_extensions import Self
|
|
13
|
+
|
|
14
|
+
__all__ = ["Self"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Custom exception hierarchy for serializers and validation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SerializerError(Exception):
|
|
11
|
+
"""
|
|
12
|
+
Standard serializer exception.
|
|
13
|
+
|
|
14
|
+
Supports both conventional exception signatures and legacy two-argument signatures:
|
|
15
|
+
- raise SerializerError("Error message")
|
|
16
|
+
- raise SerializerError(ValueError, "Error message")
|
|
17
|
+
- raise SerializerError("Error message", field="email", code="invalid")
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
*args: Any,
|
|
23
|
+
field: Optional[str] = None,
|
|
24
|
+
code: str = "invalid",
|
|
25
|
+
) -> None:
|
|
26
|
+
super().__init__(*args)
|
|
27
|
+
self.field = field
|
|
28
|
+
self.code = code
|
|
29
|
+
self.exception_class: Optional[type] = None
|
|
30
|
+
|
|
31
|
+
if len(args) == 1:
|
|
32
|
+
self.message = str(args[0])
|
|
33
|
+
self.exception_class = self.__class__
|
|
34
|
+
elif len(args) >= 2:
|
|
35
|
+
first, second = args[0], args[1]
|
|
36
|
+
if isinstance(first, type) and issubclass(first, BaseException):
|
|
37
|
+
self.exception_class = first
|
|
38
|
+
self.message = str(second)
|
|
39
|
+
else:
|
|
40
|
+
self.message = str(second)
|
|
41
|
+
self.exception_class = self.__class__
|
|
42
|
+
else:
|
|
43
|
+
self.message = "Serializer error"
|
|
44
|
+
self.exception_class = self.__class__
|
|
45
|
+
|
|
46
|
+
def to_dict(self) -> dict:
|
|
47
|
+
type_name = self.exception_class.__name__ if self.exception_class else self.__class__.__name__
|
|
48
|
+
res = {
|
|
49
|
+
"msg": self.message,
|
|
50
|
+
"type": type_name,
|
|
51
|
+
"code": self.code,
|
|
52
|
+
}
|
|
53
|
+
if self.field:
|
|
54
|
+
res["field"] = self.field
|
|
55
|
+
return res
|
|
56
|
+
|
|
57
|
+
def __str__(self) -> str:
|
|
58
|
+
return self.message
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ValidationError(SerializerError):
|
|
62
|
+
"""
|
|
63
|
+
Validation error raised when data fails schema or field-level constraints.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
detail: Any,
|
|
69
|
+
field: Optional[str] = None,
|
|
70
|
+
code: str = "invalid",
|
|
71
|
+
) -> None:
|
|
72
|
+
if isinstance(detail, (dict, list)):
|
|
73
|
+
self.detail = detail
|
|
74
|
+
super().__init__(str(detail), field=field, code=code)
|
|
75
|
+
else:
|
|
76
|
+
self.detail = str(detail)
|
|
77
|
+
super().__init__(str(detail), field=field, code=code)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Field definitions and special serializer descriptors.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Callable, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Field:
|
|
11
|
+
"""
|
|
12
|
+
Base representation of a serializer field.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
source: Optional[str] = None,
|
|
18
|
+
read_only: bool = False,
|
|
19
|
+
write_only: bool = False,
|
|
20
|
+
required: bool = True,
|
|
21
|
+
default: Any = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.source = source
|
|
24
|
+
self.read_only = read_only
|
|
25
|
+
self.write_only = write_only
|
|
26
|
+
self.required = required
|
|
27
|
+
self.default = default
|
|
28
|
+
self.field_name: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
def bind(self, field_name: str) -> None:
|
|
31
|
+
self.field_name = field_name
|
|
32
|
+
if self.source is None:
|
|
33
|
+
self.source = field_name
|
|
34
|
+
|
|
35
|
+
def get_attribute(self, instance: Any) -> Any:
|
|
36
|
+
if self.source:
|
|
37
|
+
return getattr(instance, self.source, self.default)
|
|
38
|
+
return self.default
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SerializerMethodField(Field):
|
|
42
|
+
"""
|
|
43
|
+
Read-only field that gets its value by calling a method on the serializer class.
|
|
44
|
+
Method defaults to `get_<field_name>(instance)`.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, method_name: Optional[str] = None) -> None:
|
|
48
|
+
super().__init__(read_only=True, required=False)
|
|
49
|
+
self.method_name = method_name
|
|
50
|
+
|
|
51
|
+
def bind(self, field_name: str) -> None:
|
|
52
|
+
super().bind(field_name)
|
|
53
|
+
if self.method_name is None:
|
|
54
|
+
self.method_name = f"get_{field_name}"
|
|
55
|
+
|
|
56
|
+
def to_representation(self, serializer: Any, instance: Any) -> Any:
|
|
57
|
+
method: Optional[Callable] = getattr(serializer, self.method_name, None)
|
|
58
|
+
if method is None or not callable(method):
|
|
59
|
+
raise AttributeError(
|
|
60
|
+
f"Serializer '{serializer.__class__.__name__}' is missing method '{self.method_name}'"
|
|
61
|
+
)
|
|
62
|
+
return method(instance)
|