sqlmodel-ext 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl
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.
- sqlmodel_ext/__init__.py +1 -1
- sqlmodel_ext/_compat.py +30 -0
- sqlmodel_ext/mixins/polymorphic.py +16 -0
- sqlmodel_ext/relation_load_checker.py +3737 -3645
- {sqlmodel_ext-0.3.0.dist-info → sqlmodel_ext-0.3.2.dist-info}/METADATA +1 -1
- {sqlmodel_ext-0.3.0.dist-info → sqlmodel_ext-0.3.2.dist-info}/RECORD +8 -8
- {sqlmodel_ext-0.3.0.dist-info → sqlmodel_ext-0.3.2.dist-info}/WHEEL +0 -0
- {sqlmodel_ext-0.3.0.dist-info → sqlmodel_ext-0.3.2.dist-info}/licenses/LICENSE +0 -0
sqlmodel_ext/__init__.py
CHANGED
sqlmodel_ext/_compat.py
CHANGED
|
@@ -14,6 +14,7 @@ if sys.version_info >= (3, 14):
|
|
|
14
14
|
from enum import StrEnum
|
|
15
15
|
from typing import Literal, Union, get_args, get_origin
|
|
16
16
|
|
|
17
|
+
from pydantic_core import PydanticUndefined
|
|
17
18
|
from sqlalchemy import String
|
|
18
19
|
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
|
19
20
|
|
|
@@ -27,6 +28,24 @@ if sys.version_info >= (3, 14):
|
|
|
27
28
|
import sqlmodel.main
|
|
28
29
|
_original_get_sqlalchemy_type = sqlmodel.main.get_sqlalchemy_type
|
|
29
30
|
|
|
31
|
+
def _get_explicit_sa_type(field):
|
|
32
|
+
"""
|
|
33
|
+
Extract an explicit ``sa_type`` from a SQLModel/Pydantic FieldInfo.
|
|
34
|
+
|
|
35
|
+
Matches upstream ``sqlmodel.main._get_sqlmodel_field_value`` semantics:
|
|
36
|
+
prefers ``FieldInfoMetadata.sa_type`` then falls back to the direct
|
|
37
|
+
``field.sa_type`` attribute. Returns ``PydanticUndefined`` when unset.
|
|
38
|
+
"""
|
|
39
|
+
# Prefer FieldInfoMetadata.sa_type (set via Field(sa_type=...))
|
|
40
|
+
metadata = getattr(field, 'metadata', None)
|
|
41
|
+
if metadata:
|
|
42
|
+
for item in metadata:
|
|
43
|
+
sa_type = getattr(item, 'sa_type', PydanticUndefined)
|
|
44
|
+
if sa_type is not PydanticUndefined:
|
|
45
|
+
return sa_type
|
|
46
|
+
# Fallback to direct attribute
|
|
47
|
+
return getattr(field, 'sa_type', PydanticUndefined)
|
|
48
|
+
|
|
30
49
|
def _patched_get_sqlalchemy_type(field):
|
|
31
50
|
"""
|
|
32
51
|
Fix SQLModel's get_sqlalchemy_type for Python 3.14 type issues.
|
|
@@ -34,6 +53,17 @@ if sys.version_info >= (3, 14):
|
|
|
34
53
|
Handles ForwardRef, ClassVar, Literal, Mapped, and custom types
|
|
35
54
|
that cause issubclass errors under PEP 649.
|
|
36
55
|
"""
|
|
56
|
+
# Respect explicit sa_type from Field(sa_type=...) — must take
|
|
57
|
+
# precedence over annotation-based auto-detection, matching upstream
|
|
58
|
+
# SQLModel behavior (sqlmodel/main.py::get_sqlalchemy_type checks
|
|
59
|
+
# sa_type before everything else). Without this, fields like
|
|
60
|
+
# ``options: list[str] | None = Field(sa_type=JSON)`` incorrectly
|
|
61
|
+
# get JSONB from the Union[list, None] -> JSONB branch below,
|
|
62
|
+
# breaking non-PostgreSQL dialects (e.g., SQLite in tests).
|
|
63
|
+
explicit_sa_type = _get_explicit_sa_type(field)
|
|
64
|
+
if explicit_sa_type is not PydanticUndefined:
|
|
65
|
+
return explicit_sa_type
|
|
66
|
+
|
|
37
67
|
# Check field.metadata (Pydantic-processed Annotated types)
|
|
38
68
|
metadata = getattr(field, 'metadata', None)
|
|
39
69
|
if metadata:
|
|
@@ -453,6 +453,22 @@ class AutoPolymorphicIdentityMixin:
|
|
|
453
453
|
# other subclasses' rows won't have values for these columns.
|
|
454
454
|
# Pydantic-level constraints still apply when creating specific subclasses.
|
|
455
455
|
column.nullable = True
|
|
456
|
+
|
|
457
|
+
# Clear Python-side and server-side defaults on the shared column.
|
|
458
|
+
# Otherwise, a Pydantic field default (e.g. ``some_field: int = 5``)
|
|
459
|
+
# gets propagated into Column.default by ``get_column_from_field()``.
|
|
460
|
+
# When sibling subclasses that do NOT declare this field are inserted,
|
|
461
|
+
# the attribute is absent from their model_fields and from the
|
|
462
|
+
# instance's ``__dict__``; the ORM falls back to Column.default and
|
|
463
|
+
# writes the declaring subclass's default into the sibling row,
|
|
464
|
+
# silently polluting unrelated rows across the STI table.
|
|
465
|
+
# The declaring subclass itself is unaffected: Pydantic populates the
|
|
466
|
+
# instance attribute at ``__init__`` time, and the ORM reads the
|
|
467
|
+
# value directly from the instance on flush without ever consulting
|
|
468
|
+
# Column.default.
|
|
469
|
+
column.default = None
|
|
470
|
+
column.server_default = None
|
|
471
|
+
|
|
456
472
|
parent_table.append_column(column)
|
|
457
473
|
except Exception as e:
|
|
458
474
|
logger.warning(f"Failed to create column {field_name} for {cls.__name__}: {e}")
|