sqlobjects 1.0.7__tar.gz → 1.0.8__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.8}/PKG-INFO +1 -1
  2. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/pyproject.toml +1 -1
  3. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/__init__.py +14 -5
  4. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/core.py +99 -34
  5. sqlobjects-1.0.8/sqlobjects/fields/proxies.py +486 -0
  6. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/relations/__init__.py +2 -15
  7. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/relations/descriptors.py +26 -15
  8. sqlobjects-1.0.8/sqlobjects/fields/relations/strategies.py +17 -0
  9. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/relations/utils.py +67 -13
  10. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/mixins.py +39 -8
  11. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/model.py +14 -4
  12. {sqlobjects-1.0.7 → sqlobjects-1.0.8/sqlobjects.egg-info}/PKG-INFO +1 -1
  13. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects.egg-info/SOURCES.txt +1 -1
  14. sqlobjects-1.0.7/sqlobjects/fields/proxies.py +0 -147
  15. sqlobjects-1.0.7/sqlobjects/fields/relations/proxies.py +0 -419
  16. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/LICENSE +0 -0
  17. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/README.md +0 -0
  18. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/setup.cfg +0 -0
  19. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/__init__.py +0 -0
  20. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/cascade.py +0 -0
  21. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/database/__init__.py +0 -0
  22. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/database/config.py +0 -0
  23. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/database/manager.py +0 -0
  24. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/exceptions.py +0 -0
  25. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/__init__.py +0 -0
  26. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/aggregate.py +0 -0
  27. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/base.py +0 -0
  28. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/function.py +0 -0
  29. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/mixins.py +0 -0
  30. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/scalar.py +0 -0
  31. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/subquery.py +0 -0
  32. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/expressions/terminal.py +0 -0
  33. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/functions.py +0 -0
  34. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/relations/managers.py +0 -0
  35. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/relations/prefetch.py +0 -0
  36. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/shortcuts.py +0 -0
  37. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/types/__init__.py +0 -0
  38. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/types/base.py +0 -0
  39. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/types/comparators.py +0 -0
  40. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/types/registry.py +0 -0
  41. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/fields/utils.py +0 -0
  42. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/internal/__init__.py +0 -0
  43. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/internal/operations.py +0 -0
  44. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/internal/results.py +0 -0
  45. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/metadata.py +0 -0
  46. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/objects/__init__.py +0 -0
  47. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/objects/bulk.py +0 -0
  48. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/objects/core.py +0 -0
  49. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/objects/upsert.py +0 -0
  50. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/queries/__init__.py +0 -0
  51. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/queries/builder.py +0 -0
  52. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/queries/dialect.py +0 -0
  53. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/queries/executor.py +0 -0
  54. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/queryset.py +0 -0
  55. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/session.py +0 -0
  56. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/signals.py +0 -0
  57. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/utils/__init__.py +0 -0
  58. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/utils/inspect.py +0 -0
  59. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/utils/naming.py +0 -0
  60. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/utils/pattern.py +0 -0
  61. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects/validators.py +0 -0
  62. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects.egg-info/dependency_links.txt +0 -0
  63. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects.egg-info/requires.txt +0 -0
  64. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/sqlobjects.egg-info/top_level.txt +0 -0
  65. {sqlobjects-1.0.7 → sqlobjects-1.0.8}/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.8
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.8"
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" }
@@ -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]):