sqlobjects 1.0.0__tar.gz → 1.0.1__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 (55) hide show
  1. {sqlobjects-1.0.0/sqlobjects.egg-info → sqlobjects-1.0.1}/PKG-INFO +5 -5
  2. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/README.md +4 -4
  3. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/pyproject.toml +4 -4
  4. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/database/manager.py +12 -12
  5. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/core.py +310 -31
  6. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/mixins.py +5 -0
  7. {sqlobjects-1.0.0 → sqlobjects-1.0.1/sqlobjects.egg-info}/PKG-INFO +5 -5
  8. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/LICENSE +0 -0
  9. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/setup.cfg +0 -0
  10. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/__init__.py +0 -0
  11. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/database/__init__.py +0 -0
  12. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/database/config.py +0 -0
  13. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/exceptions.py +0 -0
  14. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/__init__.py +0 -0
  15. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/aggregate.py +0 -0
  16. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/base.py +0 -0
  17. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/function.py +0 -0
  18. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/mixins.py +0 -0
  19. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/scalar.py +0 -0
  20. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/subquery.py +0 -0
  21. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/expressions/terminal.py +0 -0
  22. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/__init__.py +0 -0
  23. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/functions.py +0 -0
  24. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/proxies.py +0 -0
  25. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/relations/__init__.py +0 -0
  26. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/relations/descriptors.py +0 -0
  27. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/relations/managers.py +0 -0
  28. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/relations/proxies.py +0 -0
  29. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/relations/utils.py +0 -0
  30. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/shortcuts.py +0 -0
  31. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/types/__init__.py +0 -0
  32. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/types/base.py +0 -0
  33. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/types/comparators.py +0 -0
  34. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/types/registry.py +0 -0
  35. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/fields/utils.py +0 -0
  36. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/metadata.py +0 -0
  37. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/model.py +0 -0
  38. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/objects/__init__.py +0 -0
  39. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/objects/bulk.py +0 -0
  40. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/objects/core.py +0 -0
  41. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/queries/__init__.py +0 -0
  42. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/queries/builder.py +0 -0
  43. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/queries/executor.py +0 -0
  44. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/queryset.py +0 -0
  45. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/session.py +0 -0
  46. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/signals.py +0 -0
  47. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/utils/__init__.py +0 -0
  48. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/utils/inspect.py +0 -0
  49. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/utils/naming.py +0 -0
  50. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/utils/pattern.py +0 -0
  51. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects/validators.py +0 -0
  52. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects.egg-info/SOURCES.txt +0 -0
  53. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects.egg-info/dependency_links.txt +0 -0
  54. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects.egg-info/requires.txt +0 -0
  55. {sqlobjects-1.0.0 → sqlobjects-1.0.1}/sqlobjects.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.0.0
3
+ Version: 1.0.1
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>
@@ -258,9 +258,9 @@ user = await User.objects.using("analytics").create(username="analyst")
258
258
  async for user in User.objects.iterator(chunk_size=1000):
259
259
  await process_user(user)
260
260
 
261
- # Cache control for performance
262
- users = await User.objects.filter(User.is_active == True).all() # Uses cache
263
- live_data = await User.objects.no_cache().filter(User.status == "online").all() # Skip cache
261
+ # Field selection for performance
262
+ users = await User.objects.only("id", "username", "email").all() # Load only needed fields
263
+ live_data = await User.objects.defer("bio", "profile_image").all() # Defer heavy fields
264
264
 
265
265
  # Field-level performance optimization
266
266
  class User(ObjectModel):
@@ -336,7 +336,7 @@ uv run pytest
336
336
  See our [TODO.md](TODO.md) for planned features:
337
337
 
338
338
  - **v2.0**: Database health checks, window functions, advanced bulk operations
339
- - **v2.1**: Advanced cache management, query optimization tools
339
+ - **v2.1**: Advanced field optimization, query performance tools
340
340
  - **v2.2+**: CTE support, advanced SQL functions
341
341
 
342
342
  ## 📄 License
@@ -228,9 +228,9 @@ user = await User.objects.using("analytics").create(username="analyst")
228
228
  async for user in User.objects.iterator(chunk_size=1000):
229
229
  await process_user(user)
230
230
 
231
- # Cache control for performance
232
- users = await User.objects.filter(User.is_active == True).all() # Uses cache
233
- live_data = await User.objects.no_cache().filter(User.status == "online").all() # Skip cache
231
+ # Field selection for performance
232
+ users = await User.objects.only("id", "username", "email").all() # Load only needed fields
233
+ live_data = await User.objects.defer("bio", "profile_image").all() # Defer heavy fields
234
234
 
235
235
  # Field-level performance optimization
236
236
  class User(ObjectModel):
@@ -306,7 +306,7 @@ uv run pytest
306
306
  See our [TODO.md](TODO.md) for planned features:
307
307
 
308
308
  - **v2.0**: Database health checks, window functions, advanced bulk operations
309
- - **v2.1**: Advanced cache management, query optimization tools
309
+ - **v2.1**: Advanced field optimization, query performance tools
310
310
  - **v2.2+**: CTE support, advanced SQL functions
311
311
 
312
312
  ## 📄 License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlobjects"
3
- version = "1.0.0"
3
+ version = "1.0.1"
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" }
@@ -49,7 +49,7 @@ Changelog = "https://github.com/XtraVisionsAI/sqlobjects/blob/main/CHANGELOG.md"
49
49
  dev = [
50
50
  "pre-commit>=4.3.0",
51
51
  "pyright>=1.1.405",
52
- "ruff>=0.12.12",
52
+ "ruff>=0.13.0",
53
53
  "setuptools>=80.9.0",
54
54
  ]
55
55
  test = [
@@ -57,8 +57,8 @@ test = [
57
57
  "aiosqlite>=0.20.0",
58
58
  "asyncpg>=0.30.0",
59
59
  "pytest>=8.4.2",
60
- "pytest-asyncio>=0.24.0",
61
- "psutil>=7.0.0", # For memory monitoring in performance tests
60
+ "pytest-asyncio>=1.2.0",
61
+ "psutil>=7.1.0", # For memory monitoring in performance tests
62
62
  ]
63
63
 
64
64
  [[tool.uv.index]]
@@ -174,37 +174,37 @@ class Database:
174
174
 
175
175
  return event.listens_for(target, event_name)
176
176
 
177
- async def create_tables(self, metadata) -> None:
178
- """Create all tables defined in the metadata
177
+ async def create_tables(self, base_class) -> None:
178
+ """Create all tables defined in the model registry of SQLObjects base class
179
179
 
180
180
  Creates all tables, indexes, and constraints defined in the provided
181
181
  SQLAlchemy metadata object using the database engine.
182
182
 
183
183
  Args:
184
- metadata: SQLAlchemy metadata object containing table definitions
184
+ base_class: SQLObjects base class containing model registry
185
185
 
186
186
  Examples:
187
187
  >>> from sqlobjects.base import ObjectModel
188
- >>> await db.create_tables(ObjectModel.__registry__)
188
+ >>> await db.create_tables(ObjectModel)
189
189
  """
190
190
  async with self.engine.begin() as conn:
191
- await conn.run_sync(metadata.create_all)
191
+ await conn.run_sync(base_class.__registry__.create_all)
192
192
 
193
- async def drop_tables(self, metadata) -> None:
194
- """Drop all tables defined in the metadata
193
+ async def drop_tables(self, base_class) -> None:
194
+ """Drop all tables defined in the model registry of SQLObjects base class
195
195
 
196
196
  Drops all tables, indexes, and constraints defined in the provided
197
197
  SQLAlchemy metadata object from the database.
198
198
 
199
199
  Args:
200
- metadata: SQLAlchemy metadata object containing table definitions
200
+ base_class: SQLObjects base class containing model registry
201
201
 
202
202
  Examples:
203
203
  >>> from sqlobjects.base import ObjectModel
204
- >>> await db.drop_tables(ObjectModel.__registry__)
204
+ >>> await db.drop_tables(ObjectModel)
205
205
  """
206
206
  async with self.engine.begin() as conn:
207
- await conn.run_sync(metadata.drop_all)
207
+ await conn.run_sync(base_class.__registry__.drop_all)
208
208
 
209
209
  async def disconnect(self) -> None:
210
210
  """Disconnect database and clean up resources
@@ -305,7 +305,7 @@ class _DatabaseManager:
305
305
  >>> await DatabaseManager.create_tables(ObjectModel, "analytics")
306
306
  """
307
307
  database = cls.get_database(db_name)
308
- await database.create_tables(base_class.__registry__)
308
+ await database.create_tables(base_class)
309
309
 
310
310
  @classmethod
311
311
  async def drop_tables(cls, base_class, db_name: str | None = None) -> None:
@@ -327,7 +327,7 @@ class _DatabaseManager:
327
327
  >>> await DatabaseManager.drop_tables(ObjectModel, "analytics")
328
328
  """
329
329
  database = cls.get_database(db_name)
330
- await database.drop_tables(base_class.__registry__)
330
+ await database.drop_tables(base_class)
331
331
 
332
332
  @classmethod
333
333
  async def close(cls, db_name: str | None = None, auto_default: bool = False) -> None:
@@ -20,9 +20,36 @@ NullableT = TypeVar("NullableT")
20
20
 
21
21
 
22
22
  class Column(Generic[T]):
23
- """Field descriptor for parameter collection and ColumnAttribute creation."""
23
+ """Field descriptor for parameter collection and ColumnAttribute creation.
24
+
25
+ This class serves as a field descriptor that collects field parameters during
26
+ class definition and creates the appropriate ColumnAttribute or relationship
27
+ descriptor when the field is accessed.
28
+
29
+ Type Parameters:
30
+ T: The Python type of the field value
31
+
32
+ Attributes:
33
+ name: Field name set by __set_name__
34
+ _params: Dictionary of field parameters
35
+ _column_attribute: Created ColumnAttribute instance
36
+ _relationship_descriptor: Created relationship descriptor (if applicable)
37
+ _is_relationship: Whether this is a relationship field
38
+ _nullable: Whether the field accepts None values
39
+
40
+ Example:
41
+ >>> class User(ObjectModel):
42
+ ... name: Column[str] = StringColumn(length=50)
43
+ ... age: Column[int] = IntegerColumn(nullable=True)
44
+ """
24
45
 
25
46
  def __init__(self, **params):
47
+ """Initialize field descriptor with parameters.
48
+
49
+ Args:
50
+ **params: Field configuration parameters including type, constraints,
51
+ validation rules, and performance settings
52
+ """
26
53
  self._params = params
27
54
  self._column_attribute = None
28
55
  self._relationship_descriptor = None
@@ -32,6 +59,16 @@ class Column(Generic[T]):
32
59
  self._private_name = None
33
60
 
34
61
  def __set_name__(self, owner, name):
62
+ """Set field name and initialize appropriate descriptor.
63
+
64
+ Called automatically by Python when the field is assigned to a class.
65
+ Creates either a ColumnAttribute for database fields or a relationship
66
+ descriptor for relationship fields.
67
+
68
+ Args:
69
+ owner: The model class that owns this field
70
+ name: The field name
71
+ """
35
72
  self.name = name
36
73
  self._private_name = f"_{name}"
37
74
 
@@ -41,7 +78,12 @@ class Column(Generic[T]):
41
78
  self._setup_column(owner, name)
42
79
 
43
80
  def _setup_relationship(self, owner, name):
44
- """Set relationship field"""
81
+ """Set up relationship field descriptor.
82
+
83
+ Args:
84
+ owner: The model class that owns this field
85
+ name: The field name
86
+ """
45
87
  from .relations.descriptors import RelationshipDescriptor
46
88
 
47
89
  relationship_property = self._params.get("relationship_property")
@@ -50,9 +92,17 @@ class Column(Generic[T]):
50
92
  self._relationship_descriptor.__set_name__(owner, name)
51
93
 
52
94
  def _setup_column(self, owner, name):
53
- """Set database field"""
95
+ """Set up database column field.
96
+
97
+ Processes field parameters, applies defaults, and creates a ColumnAttribute
98
+ instance with proper type handling and parameter organization.
99
+
100
+ Args:
101
+ owner: The model class that owns this field
102
+ name: The field name
103
+ """
54
104
  params = self._params.copy()
55
- foreign_key = params.pop("foreign_key", None)
105
+ fk = params.pop("foreign_key", None)
56
106
  type_name = params.pop("type", "auto")
57
107
 
58
108
  # Process extended parameters
@@ -111,7 +161,7 @@ class Column(Generic[T]):
111
161
 
112
162
  # Create ColumnAttribute
113
163
  self._column_attribute = ColumnAttribute(
114
- name, enhanced_type, foreign_key=foreign_key, model_class=owner, **column_params
164
+ name, enhanced_type, foreign_key=fk, model_class=owner, **column_params
115
165
  )
116
166
 
117
167
  @overload
@@ -121,6 +171,18 @@ class Column(Generic[T]):
121
171
  def __get__(self, instance: Any, owner: type) -> T: ...
122
172
 
123
173
  def __get__(self, instance, owner):
174
+ """Get field value or ColumnAttribute.
175
+
176
+ Returns the ColumnAttribute when accessed on the class (for query building)
177
+ or the actual field value when accessed on an instance.
178
+
179
+ Args:
180
+ instance: Model instance or None if accessed on class
181
+ owner: The model class
182
+
183
+ Returns:
184
+ ColumnAttribute when accessed on class, field value when accessed on instance
185
+ """
124
186
  if self._is_relationship and self._relationship_descriptor:
125
187
  return self._relationship_descriptor.__get__(instance, owner)
126
188
 
@@ -136,6 +198,15 @@ class Column(Generic[T]):
136
198
  return cast(T, value)
137
199
 
138
200
  def __set__(self, instance, value):
201
+ """Set field value on instance.
202
+
203
+ Args:
204
+ instance: Model instance to set value on
205
+ value: Value to set
206
+
207
+ Raises:
208
+ AttributeError: If trying to set value on class rather than instance
209
+ """
139
210
  if self._is_relationship:
140
211
  # Relationship fields may not support direct setting
141
212
  pass
@@ -147,18 +218,6 @@ class Column(Generic[T]):
147
218
 
148
219
 
149
220
  class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
150
- def __getattr__(self, name):
151
- """Handle attribute access with proper priority
152
-
153
- First check for SQLAlchemy column attributes, then delegate to function mixin.
154
- """
155
- # First try the underlying SQLAlchemy column for its own attributes
156
- if hasattr(self.__column__, name):
157
- return getattr(self.__column__, name)
158
-
159
- # Then delegate to the function mixin for database functions
160
- return super().__getattr__(name)
161
-
162
221
  """Enhanced column attribute with SQLAlchemy CoreColumn compatibility.
163
222
 
164
223
  Extends SQLAlchemy's Column with additional functionality for validation,
@@ -180,7 +239,38 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
180
239
 
181
240
  inherit_cache = True # make use of the cache key generated by the superclass from SQLAlchemy
182
241
 
242
+ def __getattr__(self, name):
243
+ """Handle attribute access with proper priority.
244
+
245
+ First checks for SQLAlchemy column attributes, then delegates to the
246
+ function mixin for database functions like like(), ilike(), etc.
247
+
248
+ Args:
249
+ name: Attribute name to access
250
+
251
+ Returns:
252
+ Attribute value from SQLAlchemy column or function mixin
253
+
254
+ Raises:
255
+ AttributeError: If attribute is not found
256
+ """
257
+ # First try the underlying SQLAlchemy column for its own attributes
258
+ if hasattr(self.__column__, name):
259
+ return getattr(self.__column__, name)
260
+
261
+ # Then delegate to the function mixin for database functions
262
+ return super().__getattr__(name)
263
+
183
264
  def __init__(self, name, type_, foreign_key=None, *, model_class, **kwargs): # noqa
265
+ """Initialize ColumnAttribute with enhanced functionality.
266
+
267
+ Args:
268
+ name: Column name
269
+ type_: SQLAlchemy type instance
270
+ foreign_key: Foreign key constraint if applicable
271
+ model_class: The model class this column belongs to
272
+ **kwargs: Additional SQLAlchemy column parameters
273
+ """
184
274
  # Extract enhanced parameters from info dict
185
275
  info = kwargs.get("info", {})
186
276
  enhanced_params = info.get("_enhanced", {})
@@ -210,10 +300,26 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
210
300
  # Validation related
211
301
  @property
212
302
  def validators(self) -> list[Any]:
303
+ """Get list of field validators.
304
+
305
+ Returns:
306
+ List of validation functions for this field
307
+ """
213
308
  return self._enhanced_params.get("validators", [])
214
309
 
215
310
  def validate_value(self, value: Any, field_name: str) -> Any:
216
- """Validate field value using registered validators"""
311
+ """Validate field value using registered validators.
312
+
313
+ Args:
314
+ value: Value to validate
315
+ field_name: Name of the field being validated
316
+
317
+ Returns:
318
+ Validated value (may be transformed by validators)
319
+
320
+ Raises:
321
+ ValidationError: If validation fails
322
+ """
217
323
  validators = self.validators
218
324
  if validators:
219
325
  from ..validators import validate_field_value
@@ -223,16 +329,37 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
223
329
 
224
330
  # Default value related
225
331
  def get_default_factory(self) -> Callable[[], Any] | None:
332
+ """Get default value factory function.
333
+
334
+ Returns:
335
+ Callable that generates default values, or None if not set
336
+ """
226
337
  return self._enhanced_params.get("default_factory")
227
338
 
228
339
  def get_insert_default(self) -> Any:
340
+ """Get insert-only default value.
341
+
342
+ Returns:
343
+ Default value used only for INSERT operations
344
+ """
229
345
  return self._enhanced_params.get("insert_default")
230
346
 
231
347
  def has_insert_default(self) -> bool:
348
+ """Check if field has insert-only default value.
349
+
350
+ Returns:
351
+ True if insert_default is configured, False otherwise
352
+ """
232
353
  return "insert_default" in self._enhanced_params
233
354
 
234
355
  def get_effective_default(self) -> Any:
235
- """Get effective default value by priority order"""
356
+ """Get effective default value by priority order.
357
+
358
+ Checks default sources in priority order: default, default_factory, insert_default.
359
+
360
+ Returns:
361
+ The effective default value or callable, or None if no default is set
362
+ """
236
363
  if self.default is not None:
237
364
  return self.default
238
365
 
@@ -249,27 +376,59 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
249
376
  # Performance optimization related
250
377
  @property
251
378
  def is_deferred(self) -> bool:
379
+ """Check if field is configured for deferred loading.
380
+
381
+ Returns:
382
+ True if field should be loaded lazily, False otherwise
383
+ """
252
384
  return self._performance_params.get("deferred", False)
253
385
 
254
386
  @property
255
387
  def deferred_group(self) -> str | None:
388
+ """Get deferred loading group name.
389
+
390
+ Returns:
391
+ Name of the deferred loading group, or None if not grouped
392
+ """
256
393
  return self._performance_params.get("deferred_group")
257
394
 
258
395
  @property
259
396
  def has_active_history(self) -> bool:
397
+ """Check if field tracks value changes.
398
+
399
+ Returns:
400
+ True if active history tracking is enabled, False otherwise
401
+ """
260
402
  return self._performance_params.get("active_history", False)
261
403
 
262
404
  @property
263
405
  def deferred_raiseload(self) -> bool | None:
406
+ """Check if accessing deferred field should raise an error.
407
+
408
+ Returns:
409
+ True to raise error, False to allow access, None for default behavior
410
+ """
264
411
  return self._performance_params.get("deferred_raiseload")
265
412
 
266
413
  # Code generation related
267
414
  @property
268
415
  def include_in_init(self) -> bool | None:
416
+ """Check if field should be included in __init__ method.
417
+
418
+ Returns:
419
+ True to include, False to exclude, None for default behavior
420
+ """
269
421
  return self._codegen_params.get("init")
270
422
 
271
423
  def create_table_column(self, name: str) -> CoreColumn:
272
- """Create independent Column instance for Table to avoid binding conflicts"""
424
+ """Create independent SQLAlchemy Column for table creation.
425
+
426
+ Args:
427
+ name: Column name for the table
428
+
429
+ Returns:
430
+ New SQLAlchemy Column instance independent of this ColumnAttribute
431
+ """
273
432
  # Create new ForeignKey instance instead of reusing existing one
274
433
  foreign_keys = []
275
434
  if self.__column__.foreign_keys:
@@ -445,38 +604,81 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
445
604
  return FunctionExpression(other % self.__column__)
446
605
 
447
606
  def __hash__(self):
448
- """Delegate hash to underlying column for SQLAlchemy compatibility"""
607
+ """Delegate hash to underlying column for SQLAlchemy compatibility.
608
+
609
+ Returns:
610
+ Hash value from the underlying SQLAlchemy column
611
+ """
449
612
  return self.__column__.__hash__()
450
613
 
451
614
  @property
452
615
  def include_in_repr(self) -> bool | None:
616
+ """Check if field should be included in __repr__ method.
617
+
618
+ Returns:
619
+ True to include, False to exclude, None for default behavior
620
+ """
453
621
  return self._codegen_params.get("repr")
454
622
 
455
623
  @property
456
624
  def include_in_compare(self) -> bool | None:
625
+ """Check if field should be included in __eq__ method.
626
+
627
+ Returns:
628
+ True to include, False to exclude, None for default behavior
629
+ """
457
630
  return self._codegen_params.get("compare")
458
631
 
459
632
  @property
460
633
  def include_in_hash(self) -> bool | None:
634
+ """Check if field should be included in __hash__ method.
635
+
636
+ Returns:
637
+ True to include, False to exclude, None for default behavior
638
+ """
461
639
  return self._codegen_params.get("hash")
462
640
 
463
641
  @property
464
642
  def is_kw_only(self) -> bool | None:
643
+ """Check if field should be keyword-only in __init__ method.
644
+
645
+ Returns:
646
+ True for keyword-only, False for positional, None for default behavior
647
+ """
465
648
  return self._codegen_params.get("kw_only")
466
649
 
467
650
  # === General parameter access methods ===
468
651
 
469
652
  def get_param(self, category: str, name: str, default: Any = None) -> Any:
470
- """Get parameter from specified category"""
653
+ """Get parameter from specified category.
654
+
655
+ Args:
656
+ category: Parameter category ('enhanced', 'performance', 'codegen')
657
+ name: Parameter name
658
+ default: Default value if parameter not found
659
+
660
+ Returns:
661
+ Parameter value or default
662
+ """
471
663
  param_dict = getattr(self, f"_{category}_params", {})
472
664
  return param_dict.get(name, default)
473
665
 
474
666
  def get_codegen_params(self) -> dict[str, Any]:
475
- """Get code generation parameters"""
667
+ """Get code generation parameters.
668
+
669
+ Returns:
670
+ Dictionary of parameters controlling __init__, __repr__, etc. generation
671
+ """
476
672
  return self._codegen_params
477
673
 
478
674
  def get_python_type(self):
479
- """Get Python type from class annotations, similar to SQLAlchemy's approach"""
675
+ """Get Python type from class annotations.
676
+
677
+ Extracts the type parameter from Column[T] annotations for type safety.
678
+
679
+ Returns:
680
+ Python type class or None if not found
681
+ """
480
682
  if not self.model_class or not self._field_name:
481
683
  return None
482
684
 
@@ -505,7 +707,12 @@ class ColumnAttribute(ColumnAttributeFunctionMixin, Generic[T]):
505
707
  return None
506
708
 
507
709
  def get_field_metadata(self) -> dict[str, Any]:
508
- """Get complete field metadata information"""
710
+ """Get complete field metadata information.
711
+
712
+ Returns:
713
+ Dictionary containing all field metadata including type info,
714
+ constraints, and extended parameters
715
+ """
509
716
  metadata = {
510
717
  "name": self.name,
511
718
  "type": str(self.type),
@@ -569,11 +776,52 @@ def column(
569
776
  hash: bool | None = None, # noqa
570
777
  kw_only: bool | None = None,
571
778
  # Foreign key constraint
572
- foreign_key: ForeignKey | None = None,
779
+ foreign_key: ForeignKey | None = None, # noqa # shadows name
573
780
  # Type parameters (passed through **kwargs)
574
781
  **kwargs: Any,
575
782
  ) -> "Column[Any]":
576
- """Create field descriptor with new unified architecture"""
783
+ """Create a database column with specified type and parameters.
784
+
785
+ Args:
786
+ type: SQLAlchemy type name (e.g., 'string', 'integer', 'datetime')
787
+ name: Column name (usually auto-detected from field name)
788
+ primary_key: Whether this is a primary key column
789
+ nullable: Whether the column accepts NULL values
790
+ default: Static default value
791
+ index: Whether to create an index on this column
792
+ unique: Whether values must be unique
793
+ autoincrement: Auto-increment behavior for integer primary keys
794
+ doc: Documentation string
795
+ key: Alternative key name for the column
796
+ onupdate: Value to set on UPDATE operations
797
+ comment: Database comment for the column
798
+ system: Whether this is a system column
799
+ server_default: Server-side default value
800
+ server_onupdate: Server-side update value
801
+ quote: Whether to quote the column name
802
+ info: Additional metadata dictionary
803
+ default_factory: Function to generate default values
804
+ validators: List of validation functions
805
+ deferred: Whether to defer loading this column
806
+ deferred_group: Group name for deferred loading
807
+ insert_default: Default value only for INSERT operations
808
+ init: Whether to include in __init__ method
809
+ repr: Whether to include in __repr__ method
810
+ compare: Whether to include in __eq__ method
811
+ active_history: Whether to track value changes
812
+ deferred_raiseload: Whether to raise error when accessing deferred field
813
+ hash: Whether to include in __hash__ method
814
+ kw_only: Whether parameter should be keyword-only in __init__
815
+ foreign_key: Foreign key constraint
816
+ **kwargs: Additional type-specific parameters
817
+
818
+ Returns:
819
+ Column descriptor configured with the specified parameters
820
+
821
+ Example:
822
+ >>> name: Column[str] = column(type="string", length=100, nullable=False)
823
+ >>> age: Column[int] = column(type="integer", default=0, validators=[validate_range(0, 150)])
824
+ """
577
825
  # Collect all parameters
578
826
  all_params = {
579
827
  "type": type,
@@ -617,7 +865,14 @@ def column(
617
865
 
618
866
 
619
867
  def _extract_column_params(kwargs: dict) -> dict:
620
- """Extract SQLAlchemy Column parameters"""
868
+ """Extract parameters relevant to SQLAlchemy Column creation.
869
+
870
+ Args:
871
+ kwargs: All field parameters
872
+
873
+ Returns:
874
+ Dictionary containing only column-related parameters
875
+ """
621
876
  column_param_names = {
622
877
  "primary_key",
623
878
  "nullable",
@@ -639,7 +894,14 @@ def _extract_column_params(kwargs: dict) -> dict:
639
894
 
640
895
 
641
896
  def _extract_type_params(kwargs: dict) -> dict:
642
- """Extract type-specific parameters"""
897
+ """Extract parameters relevant to SQLAlchemy type creation.
898
+
899
+ Args:
900
+ kwargs: All field parameters
901
+
902
+ Returns:
903
+ Dictionary containing only type-related parameters
904
+ """
643
905
  column_param_names = {
644
906
  "primary_key",
645
907
  "nullable",
@@ -661,7 +923,15 @@ def _extract_type_params(kwargs: dict) -> dict:
661
923
 
662
924
 
663
925
  def _apply_codegen_defaults(codegen_params: dict, column_kwargs: dict) -> dict:
664
- """Apply default values for code generation parameters"""
926
+ """Apply intelligent defaults for code generation parameters.
927
+
928
+ Args:
929
+ codegen_params: User-specified code generation parameters
930
+ column_kwargs: Column configuration for determining defaults
931
+
932
+ Returns:
933
+ Complete code generation parameters with defaults applied
934
+ """
665
935
  defaults = {"init": True, "repr": True, "compare": False, "hash": None, "kw_only": False}
666
936
 
667
937
  # Primary key fields: don't participate in initialization, but participate in comparison and display
@@ -689,7 +959,16 @@ def _resolve_default_value(
689
959
  default_factory: Callable[[], Any] | None,
690
960
  insert_default: Any,
691
961
  ) -> Any:
692
- """Resolve default value priority: default > default_factory > insert_default"""
962
+ """Resolve final default value from multiple sources.
963
+
964
+ Args:
965
+ default: Static default value
966
+ default_factory: Factory function for generating defaults
967
+ insert_default: Insert-only default value
968
+
969
+ Returns:
970
+ The resolved default value to use
971
+ """
693
972
  if default is not None:
694
973
  return default
695
974
 
@@ -9,6 +9,10 @@ from .fields.utils import get_column_from_field, is_field_definition
9
9
  from .session import AsyncSession, get_session
10
10
 
11
11
 
12
+ if TYPE_CHECKING:
13
+ from .metadata import ModelRegistry
14
+
15
+
12
16
  class _StateManager:
13
17
  """Unified state management for model instances."""
14
18
 
@@ -43,6 +47,7 @@ class BaseMixin:
43
47
 
44
48
  if TYPE_CHECKING:
45
49
  __table__: ClassVar[Table]
50
+ __registry__: ClassVar["ModelRegistry"]
46
51
 
47
52
  def __init__(self):
48
53
  """Initialize state manager if not already present."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.0.0
3
+ Version: 1.0.1
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>
@@ -258,9 +258,9 @@ user = await User.objects.using("analytics").create(username="analyst")
258
258
  async for user in User.objects.iterator(chunk_size=1000):
259
259
  await process_user(user)
260
260
 
261
- # Cache control for performance
262
- users = await User.objects.filter(User.is_active == True).all() # Uses cache
263
- live_data = await User.objects.no_cache().filter(User.status == "online").all() # Skip cache
261
+ # Field selection for performance
262
+ users = await User.objects.only("id", "username", "email").all() # Load only needed fields
263
+ live_data = await User.objects.defer("bio", "profile_image").all() # Defer heavy fields
264
264
 
265
265
  # Field-level performance optimization
266
266
  class User(ObjectModel):
@@ -336,7 +336,7 @@ uv run pytest
336
336
  See our [TODO.md](TODO.md) for planned features:
337
337
 
338
338
  - **v2.0**: Database health checks, window functions, advanced bulk operations
339
- - **v2.1**: Advanced cache management, query optimization tools
339
+ - **v2.1**: Advanced field optimization, query performance tools
340
340
  - **v2.2+**: CTE support, advanced SQL functions
341
341
 
342
342
  ## 📄 License
File without changes
File without changes