Skip to content

Commit 2c8b044

Browse files
authored
fix: property correction (#539)
Property correction for updates
1 parent b1da7af commit 2c8b044

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

advanced_alchemy/repository/_util.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from sqlalchemy.sql import ColumnElement, ColumnExpressionArgument
2323
from sqlalchemy.sql.base import ExecutableOption
2424
from sqlalchemy.sql.dml import ReturningDelete, ReturningUpdate
25+
from sqlalchemy.sql.elements import Label
2526
from typing_extensions import TypeAlias
2627

2728
from advanced_alchemy.base import ModelProtocol
@@ -356,9 +357,14 @@ def column_has_defaults(column: Any) -> bool:
356357
Returns:
357358
bool: True if the column has any type of default or update handler
358359
"""
360+
# Label objects (from column_property) don't have default/onupdate attributes
361+
# Return False for these as they represent computed values, not defaulted columns
362+
if isinstance(column, Label):
363+
return False
364+
# Use defensive attribute checking for safety with other column-like objects
359365
return (
360-
column.default is not None
361-
or column.server_default is not None
362-
or column.onupdate is not None
363-
or column.server_onupdate is not None
366+
getattr(column, "default", None) is not None
367+
or getattr(column, "server_default", None) is not None
368+
or getattr(column, "onupdate", None) is not None
369+
or getattr(column, "server_onupdate", None) is not None
364370
)

tests/unit/test_repository.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,60 @@ def test_column_with_empty_string_default() -> None:
13201320
assert column_has_defaults(mock_column) is True
13211321

13221322

1323+
def test_column_property_label_object() -> None:
1324+
"""Test column_property Label objects return False for column_has_defaults."""
1325+
from sqlalchemy.sql.elements import Label
1326+
1327+
# Create a Label object similar to what column_property creates
1328+
mock_label = MagicMock(spec=Label)
1329+
1330+
# Label objects don't have default/onupdate attributes, but if they did,
1331+
# they would raise AttributeError when accessed
1332+
assert column_has_defaults(mock_label) is False
1333+
1334+
1335+
def test_column_property_with_real_label() -> None:
1336+
"""Test column_has_defaults with an actual Label object from SQLAlchemy."""
1337+
from sqlalchemy import literal_column
1338+
from sqlalchemy.sql.elements import Label
1339+
1340+
# Create a real Label object like column_property would create
1341+
label_obj = literal_column("test_value").label("test_column") # type: ignore[var-annotated]
1342+
assert isinstance(label_obj, Label)
1343+
1344+
# This should return False and not raise AttributeError
1345+
assert column_has_defaults(label_obj) is False
1346+
1347+
1348+
def test_column_object_without_default_attributes() -> None:
1349+
"""Test column_has_defaults with object missing some attributes."""
1350+
1351+
# Create an object that only has some of the expected attributes
1352+
class PartialColumn:
1353+
def __init__(self) -> None:
1354+
self.default = "test_default"
1355+
# Missing server_default, onupdate, server_onupdate attributes
1356+
1357+
partial_column = PartialColumn()
1358+
1359+
# Should return True based on the default attribute, even though others are missing
1360+
assert column_has_defaults(partial_column) is True
1361+
1362+
1363+
def test_column_object_with_no_default_attributes() -> None:
1364+
"""Test column_has_defaults with object missing all attributes."""
1365+
1366+
# Create an object that has none of the expected attributes
1367+
class MinimalColumn:
1368+
def __init__(self) -> None:
1369+
self.name = "test_column"
1370+
1371+
minimal_column = MinimalColumn()
1372+
1373+
# Should return False since no default attributes are present
1374+
assert column_has_defaults(minimal_column) is False
1375+
1376+
13231377
def test_model_from_dict_includes_relationship_attributes() -> None:
13241378
"""Test that model_from_dict includes relationship attributes from __mapper__.attrs.keys()."""
13251379
from tests.fixtures.uuid.models import UUIDAuthor

0 commit comments

Comments
 (0)