sqlobjects 1.0.7__tar.gz → 1.0.9__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.
Files changed (65) hide show
  1. {sqlobjects-1.0.7/sqlobjects.egg-info → sqlobjects-1.0.9}/PKG-INFO +1 -1
  2. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/pyproject.toml +1 -1
  3. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/database/manager.py +54 -26
  4. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/__init__.py +14 -5
  5. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/core.py +99 -34
  6. sqlobjects-1.0.9/sqlobjects/fields/proxies.py +486 -0
  7. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/relations/__init__.py +2 -15
  8. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/relations/descriptors.py +26 -15
  9. sqlobjects-1.0.9/sqlobjects/fields/relations/strategies.py +17 -0
  10. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/relations/utils.py +67 -13
  11. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/mixins.py +39 -8
  12. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/model.py +14 -4
  13. {sqlobjects-1.0.7 → sqlobjects-1.0.9/sqlobjects.egg-info}/PKG-INFO +1 -1
  14. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects.egg-info/SOURCES.txt +1 -1
  15. sqlobjects-1.0.7/sqlobjects/fields/proxies.py +0 -147
  16. sqlobjects-1.0.7/sqlobjects/fields/relations/proxies.py +0 -419
  17. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/LICENSE +0 -0
  18. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/README.md +0 -0
  19. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/setup.cfg +0 -0
  20. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/__init__.py +0 -0
  21. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/cascade.py +0 -0
  22. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/database/__init__.py +0 -0
  23. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/database/config.py +0 -0
  24. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/exceptions.py +0 -0
  25. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/__init__.py +0 -0
  26. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/aggregate.py +0 -0
  27. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/base.py +0 -0
  28. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/function.py +0 -0
  29. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/mixins.py +0 -0
  30. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/scalar.py +0 -0
  31. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/subquery.py +0 -0
  32. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/expressions/terminal.py +0 -0
  33. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/functions.py +0 -0
  34. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/relations/managers.py +0 -0
  35. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/relations/prefetch.py +0 -0
  36. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/shortcuts.py +0 -0
  37. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/types/__init__.py +0 -0
  38. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/types/base.py +0 -0
  39. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/types/comparators.py +0 -0
  40. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/types/registry.py +0 -0
  41. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/fields/utils.py +0 -0
  42. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/internal/__init__.py +0 -0
  43. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/internal/operations.py +0 -0
  44. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/internal/results.py +0 -0
  45. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/metadata.py +0 -0
  46. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/objects/__init__.py +0 -0
  47. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/objects/bulk.py +0 -0
  48. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/objects/core.py +0 -0
  49. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/objects/upsert.py +0 -0
  50. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/queries/__init__.py +0 -0
  51. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/queries/builder.py +0 -0
  52. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/queries/dialect.py +0 -0
  53. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/queries/executor.py +0 -0
  54. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/queryset.py +0 -0
  55. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/session.py +0 -0
  56. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/signals.py +0 -0
  57. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/utils/__init__.py +0 -0
  58. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/utils/inspect.py +0 -0
  59. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/utils/naming.py +0 -0
  60. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/utils/pattern.py +0 -0
  61. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects/validators.py +0 -0
  62. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects.egg-info/dependency_links.txt +0 -0
  63. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects.egg-info/requires.txt +0 -0
  64. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/sqlobjects.egg-info/top_level.txt +0 -0
  65. {sqlobjects-1.0.7 → sqlobjects-1.0.9}/tests/test_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
5
  Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
6
  Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlobjects"
3
- version = "1.0.7"
3
+ version = "1.0.9"
4
4
  description = "Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -174,37 +174,49 @@ class Database:
174
174
 
175
175
  return event.listens_for(target, event_name)
176
176
 
177
- async def create_tables(self, base_class) -> None:
178
- """Create all tables defined in the model registry of SQLObjects base class
177
+ async def create_tables(self, base_class, tables: list[type] | None = None) -> None:
178
+ """Create tables defined in the model registry of SQLObjects base class
179
179
 
180
- Creates all tables, indexes, and constraints defined in the provided
180
+ Creates tables, indexes, and constraints defined in the provided
181
181
  SQLAlchemy metadata object using the database engine.
182
182
 
183
183
  Args:
184
184
  base_class: SQLObjects base class containing model registry
185
+ tables: List of model classes to create tables for, creates all if None
185
186
 
186
187
  Examples:
187
188
  >>> from sqlobjects.base import ObjectModel
188
- >>> await db.create_tables(ObjectModel)
189
+ >>> await db.create_tables(ObjectModel) # Create all tables
190
+ >>> await db.create_tables(ObjectModel, [User, Post]) # Create specific tables
189
191
  """
190
192
  async with self.engine.begin() as conn:
191
- await conn.run_sync(base_class.__registry__.create_all)
193
+ if tables is None:
194
+ await conn.run_sync(base_class.__registry__.create_all)
195
+ else:
196
+ table_objects = [model.__table__ for model in tables]
197
+ await conn.run_sync(base_class.__registry__.create_all, tables=table_objects)
192
198
 
193
- async def drop_tables(self, base_class) -> None:
194
- """Drop all tables defined in the model registry of SQLObjects base class
199
+ async def drop_tables(self, base_class, tables: list[type] | None = None) -> None:
200
+ """Drop tables defined in the model registry of SQLObjects base class
195
201
 
196
- Drops all tables, indexes, and constraints defined in the provided
202
+ Drops tables, indexes, and constraints defined in the provided
197
203
  SQLAlchemy metadata object from the database.
198
204
 
199
205
  Args:
200
206
  base_class: SQLObjects base class containing model registry
207
+ tables: List of model classes to drop tables for, drops all if None
201
208
 
202
209
  Examples:
203
210
  >>> from sqlobjects.base import ObjectModel
204
- >>> await db.drop_tables(ObjectModel)
211
+ >>> await db.drop_tables(ObjectModel) # Drop all tables
212
+ >>> await db.drop_tables(ObjectModel, [User, Post]) # Drop specific tables
205
213
  """
206
214
  async with self.engine.begin() as conn:
207
- await conn.run_sync(base_class.__registry__.drop_all)
215
+ if tables is None:
216
+ await conn.run_sync(base_class.__registry__.drop_all)
217
+ else:
218
+ table_objects = [model.__table__ for model in tables]
219
+ await conn.run_sync(base_class.__registry__.drop_all, tables=table_objects)
208
220
 
209
221
  async def disconnect(self) -> None:
210
222
  """Disconnect database and clean up resources
@@ -286,15 +298,16 @@ class _DatabaseManager:
286
298
  return database.engine
287
299
 
288
300
  @classmethod
289
- async def create_tables(cls, base_class, db_name: str | None = None) -> None:
290
- """Create all tables defined in the base class registry
301
+ async def create_tables(cls, base_class, db_name: str | None = None, tables: list[type] | None = None) -> None:
302
+ """Create tables defined in the base class registry
291
303
 
292
- Creates all tables, indexes, and constraints for models registered
304
+ Creates tables, indexes, and constraints for models registered
293
305
  in the base class registry using the specified database connection.
294
306
 
295
307
  Args:
296
308
  base_class: SQLObjects base class containing model registry
297
309
  db_name: Database name to use, uses default database if None
310
+ tables: List of model classes to create tables for, creates all if None
298
311
 
299
312
  Raises:
300
313
  ValueError: When specified database does not exist
@@ -303,20 +316,22 @@ class _DatabaseManager:
303
316
  >>> from sqlobjects.base import ObjectModel
304
317
  >>> await DatabaseManager.create_tables(ObjectModel)
305
318
  >>> await DatabaseManager.create_tables(ObjectModel, "analytics")
319
+ >>> await DatabaseManager.create_tables(ObjectModel, tables=[User, Post])
306
320
  """
307
321
  database = cls.get_database(db_name)
308
- await database.create_tables(base_class)
322
+ await database.create_tables(base_class, tables)
309
323
 
310
324
  @classmethod
311
- async def drop_tables(cls, base_class, db_name: str | None = None) -> None:
312
- """Drop all tables defined in the base class registry
325
+ async def drop_tables(cls, base_class, db_name: str | None = None, tables: list[type] | None = None) -> None:
326
+ """Drop tables defined in the base class registry
313
327
 
314
- Drops all tables, indexes, and constraints for models registered
328
+ Drops tables, indexes, and constraints for models registered
315
329
  in the base class registry from the specified database connection.
316
330
 
317
331
  Args:
318
332
  base_class: SQLObjects base class containing model registry
319
333
  db_name: Database name to use, uses default database if None
334
+ tables: List of model classes to drop tables for, drops all if None
320
335
 
321
336
  Raises:
322
337
  ValueError: When specified database does not exist
@@ -325,9 +340,10 @@ class _DatabaseManager:
325
340
  >>> from sqlobjects.base import ObjectModel
326
341
  >>> await DatabaseManager.drop_tables(ObjectModel)
327
342
  >>> await DatabaseManager.drop_tables(ObjectModel, "analytics")
343
+ >>> await DatabaseManager.drop_tables(ObjectModel, tables=[User, Post])
328
344
  """
329
345
  database = cls.get_database(db_name)
330
- await database.drop_tables(base_class)
346
+ await database.drop_tables(base_class, tables)
331
347
 
332
348
  @classmethod
333
349
  async def close(cls, db_name: str | None = None, auto_default: bool = False) -> None:
@@ -476,36 +492,48 @@ async def init_dbs(
476
492
  return tuple(db_instances)
477
493
 
478
494
 
479
- async def create_tables(base_class, db_name: str | None = None) -> None:
480
- """Create all tables defined in the base class registry
495
+ async def create_tables(base_class, db_name: str | None = None, tables: list[type] | None = None) -> None:
496
+ """Create tables defined in the base class registry
481
497
 
482
- Creates all tables, indexes, and constraints for models registered
498
+ Creates tables, indexes, and constraints for models registered
483
499
  in the base class registry using the specified database connection.
484
500
 
485
501
  Args:
486
502
  base_class: SQLObjects base class containing model registry
487
503
  db_name: Name of the database, uses default if None
504
+ tables: List of model classes to create tables for, creates all if None
488
505
 
489
506
  Raises:
490
507
  ValueError: When specified database does not exist
508
+
509
+ Examples:
510
+ >>> from sqlobjects.model import ObjectModel
511
+ >>> await create_tables(ObjectModel) # Create all tables
512
+ >>> await create_tables(ObjectModel, tables=[User, Post]) # Create specific tables
491
513
  """
492
- await _DatabaseManager.create_tables(base_class, db_name)
514
+ await _DatabaseManager.create_tables(base_class, db_name, tables)
493
515
 
494
516
 
495
- async def drop_tables(base_class, db_name: str | None = None) -> None:
496
- """Drop all tables defined in the base class registry
517
+ async def drop_tables(base_class, db_name: str | None = None, tables: list[type] | None = None) -> None:
518
+ """Drop tables defined in the base class registry
497
519
 
498
- Drops all tables, indexes, and constraints for models registered
520
+ Drops tables, indexes, and constraints for models registered
499
521
  in the base class registry from the specified database connection.
500
522
 
501
523
  Args:
502
524
  base_class: SQLObjects base class containing model registry
503
525
  db_name: Name of the database, uses default if None
526
+ tables: List of model classes to drop tables for, drops all if None
504
527
 
505
528
  Raises:
506
529
  ValueError: When specified database does not exist
530
+
531
+ Examples:
532
+ >>> from sqlobjects.model import ObjectModel
533
+ >>> await drop_tables(ObjectModel) # Drop all tables
534
+ >>> await drop_tables(ObjectModel, tables=[User, Post]) # Drop specific tables
507
535
  """
508
- await _DatabaseManager.drop_tables(base_class, db_name)
536
+ await _DatabaseManager.drop_tables(base_class, db_name, tables)
509
537
 
510
538
 
511
539
  async def close_db(db_name: str | None = None, auto_default: bool = False) -> None:
@@ -1,6 +1,12 @@
1
- from .core import Column, ColumnAttribute, column
1
+ from .core import Column, ColumnAttribute, Related, column
2
2
  from .functions import computed, foreign_key, identity
3
- from .proxies import DeferredFieldProxy, RelationFieldProxy
3
+ from .proxies import (
4
+ # Backward compatibility aliases
5
+ DeferredObject,
6
+ ManyToManyRelation,
7
+ OneToManyRelation,
8
+ RelatedObject,
9
+ )
4
10
  from .relations import M2MTable, relationship
5
11
  from .shortcuts import (
6
12
  ArrayColumn,
@@ -25,6 +31,7 @@ __all__ = [
25
31
  # Core field system
26
32
  "Column",
27
33
  "ColumnAttribute",
34
+ "Related",
28
35
  "column",
29
36
  # Shortcut field classes
30
37
  "StringColumn",
@@ -48,9 +55,11 @@ __all__ = [
48
55
  # Relationship system
49
56
  "M2MTable",
50
57
  "relationship",
51
- # Proxy system
52
- "DeferredFieldProxy",
53
- "RelationFieldProxy",
58
+ # Public proxy interfaces
59
+ "DeferredObject",
60
+ "RelatedObject",
61
+ "OneToManyRelation",
62
+ "ManyToManyRelation",
54
63
  # Utility functions
55
64
  "extract_field_metadata",
56
65
  "get_deferred_fields",
@@ -1,21 +1,112 @@
1
1
  """Core field classes for SQLObjects"""
2
2
 
3
3
  from collections.abc import Callable
4
- from typing import Any, Generic, TypeVar, Union, cast, get_args, get_origin, overload
4
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast, get_args, get_origin, overload
5
5
 
6
6
  from sqlalchemy import Column as CoreColumn
7
7
  from sqlalchemy import ForeignKey
8
8
  from sqlalchemy.sql.elements import ColumnElement
9
9
 
10
+ from sqlobjects.fields.proxies import RelatedCollection, RelatedObject
11
+
10
12
  from ..cascade import OnDelete
11
13
  from ..expressions.mixins import ColumnAttributeFunctionMixin
12
14
  from .types import create_type_instance
13
15
 
14
16
 
17
+ if TYPE_CHECKING:
18
+ pass
19
+
20
+
15
21
  T = TypeVar("T")
16
22
  NullableT = TypeVar("NullableT")
17
23
 
18
24
 
25
+ class Related(Generic[T]):
26
+ """Relationship field container - returns appropriate relationship proxy.
27
+
28
+ This class serves as a type container for relationship fields, providing
29
+ clear type hints while delegating actual behavior to RelationshipDescriptor.
30
+
31
+ Type Parameters:
32
+ T: The related model type (single object or list)
33
+
34
+ Example:
35
+ >>> class User(ObjectModel):
36
+ ... posts: Related[list["Post"]] = relationship("Post")
37
+ ... profile: Related["Profile"] = relationship("Profile", uselist=False)
38
+ """
39
+
40
+ def __init__(self, **params):
41
+ """Initialize relationship field container.
42
+
43
+ Args:
44
+ **params: Relationship configuration parameters
45
+ """
46
+ self._params = params
47
+ self._is_relationship = True
48
+ self._relationship_descriptor = None
49
+ self.name = None
50
+
51
+ def __set_name__(self, owner, name):
52
+ """Set field name and create relationship descriptor.
53
+
54
+ Args:
55
+ owner: The model class that owns this field
56
+ name: The field name
57
+ """
58
+ self.name = name
59
+ self._setup_relationship(owner, name)
60
+
61
+ def _setup_relationship(self, owner, name):
62
+ """Set up relationship field descriptor.
63
+
64
+ Args:
65
+ owner: The model class that owns this field
66
+ name: The field name
67
+ """
68
+ from .relations.descriptors import RelationshipDescriptor
69
+
70
+ relationship_property = self._params.get("relationship_property")
71
+ if relationship_property:
72
+ # Set M2M definition if provided
73
+ m2m_def = self._params.get("m2m_definition")
74
+ if m2m_def:
75
+ relationship_property.m2m_definition = m2m_def
76
+ relationship_property.is_many_to_many = True
77
+
78
+ self._relationship_descriptor = RelationshipDescriptor(relationship_property)
79
+ self._relationship_descriptor.__set_name__(owner, name)
80
+
81
+ @overload
82
+ def __get__(self, instance: None, owner: type) -> "Related[T]": ...
83
+
84
+ @overload
85
+ def __get__(self: "Related[list[Any]]", instance: Any, owner: type) -> "RelatedCollection[Any]": ...
86
+
87
+ @overload
88
+ def __get__(self, instance: Any, owner: type) -> "RelatedObject[T]": ...
89
+
90
+ def __get__(self, instance, owner) -> "Related[T] | RelatedCollection[Any] | RelatedObject[T] | None":
91
+ """Fallback descriptor - should not be called in normal usage.
92
+
93
+ ModelProcessor metaclass extracts RelationshipDescriptor and replaces
94
+ this Related instance, so this method is only called if setup fails.
95
+
96
+ Args:
97
+ instance: Model instance or None for class access
98
+ owner: The model class
99
+
100
+ Returns:
101
+ RelationshipDescriptor or None as fallback
102
+ """
103
+ if self._relationship_descriptor:
104
+ return self._relationship_descriptor.__get__(instance, owner)
105
+ if instance is None:
106
+ return self
107
+ return None
108
+
109
+
19
110
  class Column(Generic[T]):
20
111
  """Field descriptor for parameter collection and ColumnAttribute creation.
21
112
 
@@ -56,11 +147,10 @@ class Column(Generic[T]):
56
147
  self._private_name = None
57
148
 
58
149
  def __set_name__(self, owner, name):
59
- """Set field name and initialize appropriate descriptor.
150
+ """Set field name and initialize column descriptor.
60
151
 
61
152
  Called automatically by Python when the field is assigned to a class.
62
- Creates either a ColumnAttribute for database fields or a relationship
63
- descriptor for relationship fields.
153
+ Creates ColumnAttribute for database fields.
64
154
 
65
155
  Args:
66
156
  owner: The model class that owns this field
@@ -68,25 +158,7 @@ class Column(Generic[T]):
68
158
  """
69
159
  self.name = name
70
160
  self._private_name = f"_{name}"
71
-
72
- if self._is_relationship:
73
- self._setup_relationship(owner, name)
74
- else:
75
- self._setup_column(owner, name)
76
-
77
- def _setup_relationship(self, owner, name):
78
- """Set up relationship field descriptor.
79
-
80
- Args:
81
- owner: The model class that owns this field
82
- name: The field name
83
- """
84
- from .relations.descriptors import RelationshipDescriptor
85
-
86
- relationship_property = self._params.get("relationship_property")
87
- if relationship_property:
88
- self._relationship_descriptor = RelationshipDescriptor(relationship_property)
89
- self._relationship_descriptor.__set_name__(owner, name)
161
+ self._setup_column(owner, name)
90
162
 
91
163
  def _setup_column(self, owner, name):
92
164
  """Set up database column field.
@@ -197,9 +269,6 @@ class Column(Generic[T]):
197
269
  Returns:
198
270
  ColumnAttribute when accessed on class, field value when accessed on instance
199
271
  """
200
- if self._is_relationship and self._relationship_descriptor:
201
- return self._relationship_descriptor.__get__(instance, owner)
202
-
203
272
  if instance is None:
204
273
  return self._column_attribute
205
274
  else:
@@ -221,14 +290,10 @@ class Column(Generic[T]):
221
290
  Raises:
222
291
  AttributeError: If trying to set value on class rather than instance
223
292
  """
224
- if self._is_relationship:
225
- # Relationship fields may not support direct setting
226
- pass
227
- else:
228
- if instance is None:
229
- raise AttributeError("Cannot set attribute on class")
230
- private_name = self._private_name or f"_{self.name}"
231
- setattr(instance, private_name, value)
293
+ if instance is None:
294
+ raise AttributeError("Cannot set attribute on class")
295
+ private_name = self._private_name or f"_{self.name}"
296
+ setattr(instance, private_name, value)
232
297
 
233
298
 
234
299
  class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):