Skip to content

Commit 76d72cd

Browse files
committed
✨ Add support for safe access to PK int autotypes
1 parent 3b889e0 commit 76d72cd

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

sqlmodel/_compat.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,33 @@ def _calculate_keys(
219219
) -> Optional[AbstractSet[str]]: # pragma: no cover
220220
return None
221221

222+
def validate_access_primary_key_autotype(
223+
self: InstanceOrType["SQLModel"], name: str, value: Any
224+
) -> None:
225+
"""
226+
Pydantic v2
227+
Validates if the attribute being accessed is a primary key with an auto type and has not been set.
228+
229+
Args:
230+
self (InstanceOrType["SQLModel"]): The instance or type of SQLModel.
231+
name (str): The name of the attribute being accessed.
232+
value (Any): The value of the attribute being accessed.
233+
234+
Raises:
235+
ValueError: If the attribute is a primary key with an auto type and has not been set.
236+
237+
Returns:
238+
None
239+
"""
240+
if name != "model_fields":
241+
model_fields = object.__getattribute__(self, "model_fields")
242+
field = model_fields.get(name)
243+
if field is not None and isinstance(field, FieldInfo):
244+
if field.primary_key and field.annotation is int and value is None:
245+
raise ValueError(
246+
f"Primary key attribute '{name}' has not been set, please commit() it first."
247+
)
248+
222249
def sqlmodel_table_construct(
223250
*,
224251
self_instance: _TSQLModel,
@@ -499,6 +526,37 @@ def _calculate_keys(
499526

500527
return keys
501528

529+
def validate_access_primary_key_autotype(
530+
self: InstanceOrType["SQLModel"], name: str, value: Any
531+
) -> None:
532+
"""
533+
Pydantic v1
534+
Validates if the attribute being accessed is a primary key with an auto type and has not been set.
535+
536+
Args:
537+
self (InstanceOrType["SQLModel"]): The instance or type of SQLModel.
538+
name (str): The name of the attribute being accessed.
539+
value (Any): The value of the attribute being accessed.
540+
541+
Raises:
542+
ValueError: If the attribute is a primary key with an auto type and has not been set.
543+
544+
Returns:
545+
None
546+
"""
547+
if name != "__fields__":
548+
fields = object.__getattribute__(self, "__fields__")
549+
field = fields.get(name)
550+
if field is not None and isinstance(field.field_info, FieldInfo):
551+
if (
552+
field.field_info.primary_key
553+
and field.annotation is int
554+
and value is None
555+
):
556+
raise ValueError(
557+
f"Primary key attribute '{name}' has not been set, please commit() it first."
558+
)
559+
502560
def sqlmodel_validate(
503561
cls: Type[_TSQLModel],
504562
obj: Any,

sqlmodel/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
set_config_value,
8080
sqlmodel_init,
8181
sqlmodel_validate,
82+
validate_access_primary_key_autotype,
8283
)
8384
from .sql.sqltypes import GUID, AutoString
8485

@@ -732,6 +733,12 @@ def __setattr__(self, name: str, value: Any) -> None:
732733
if name not in self.__sqlmodel_relationships__:
733734
super().__setattr__(name, value)
734735

736+
def __getattribute__(self, name: str) -> Any:
737+
# Access attributes safely using object.__getattribute__ to avoid recursion
738+
value = object.__getattribute__(self, name)
739+
validate_access_primary_key_autotype(self, name, value)
740+
return value
741+
735742
def __repr_args__(self) -> Sequence[Tuple[Optional[str], Any]]:
736743
# Don't show SQLAlchemy private attributes
737744
return [

0 commit comments

Comments
 (0)