sqlobjects 1.5.0__tar.gz → 1.7.0__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 (80) hide show
  1. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/CHANGELOG.md +16 -0
  2. {sqlobjects-1.5.0/sqlobjects.egg-info → sqlobjects-1.7.0}/PKG-INFO +1 -1
  3. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/pyproject.toml +1 -1
  4. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/function.py +45 -8
  5. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/types/comparators.py +8 -2
  6. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/metadata.py +55 -2
  7. {sqlobjects-1.5.0 → sqlobjects-1.7.0/sqlobjects.egg-info}/PKG-INFO +1 -1
  8. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/LICENSE +0 -0
  9. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/README.md +0 -0
  10. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/01-database-session-guide.md +0 -0
  11. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/02-model-definition-guide.md +0 -0
  12. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/03-query-operations-guide.md +0 -0
  13. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/04-crud-operations-guide.md +0 -0
  14. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/05-relationships-guide.md +0 -0
  15. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/06-validation-signals-guide.md +0 -0
  16. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/07-performance-guide.md +0 -0
  17. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/docs/rules/README.md +0 -0
  18. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/setup.cfg +0 -0
  19. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/__init__.py +0 -0
  20. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/_install_rules.py +0 -0
  21. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/cascade.py +0 -0
  22. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/contrib/__init__.py +0 -0
  23. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/contrib/asgi.py +0 -0
  24. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/contrib/fastapi.py +0 -0
  25. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/database/__init__.py +0 -0
  26. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/database/config.py +0 -0
  27. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/database/manager.py +0 -0
  28. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/exceptions.py +0 -0
  29. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/__init__.py +0 -0
  30. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/aggregate.py +0 -0
  31. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/base.py +0 -0
  32. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/cte.py +0 -0
  33. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/explain.py +0 -0
  34. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/mixins.py +0 -0
  35. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/scalar.py +0 -0
  36. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/subquery.py +0 -0
  37. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/terminal.py +0 -0
  38. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/expressions/window.py +0 -0
  39. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/__init__.py +0 -0
  40. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/core.py +0 -0
  41. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/functions.py +0 -0
  42. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/proxies.py +0 -0
  43. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/__init__.py +0 -0
  44. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/descriptors.py +0 -0
  45. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/managers.py +0 -0
  46. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/prefetch.py +0 -0
  47. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/strategies.py +0 -0
  48. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/relations/utils.py +0 -0
  49. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/shortcuts.py +0 -0
  50. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/types/__init__.py +0 -0
  51. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/types/base.py +0 -0
  52. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/types/registry.py +0 -0
  53. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/fields/utils.py +0 -0
  54. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/internal/__init__.py +0 -0
  55. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/internal/operations.py +0 -0
  56. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/internal/results.py +0 -0
  57. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/mixins.py +0 -0
  58. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/model.py +0 -0
  59. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/objects/__init__.py +0 -0
  60. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/objects/bulk.py +0 -0
  61. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/objects/core.py +0 -0
  62. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/objects/upsert.py +0 -0
  63. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/queries/__init__.py +0 -0
  64. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/queries/builder.py +0 -0
  65. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/queries/dialect.py +0 -0
  66. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/queries/executor.py +0 -0
  67. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/queryset.py +0 -0
  68. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/session.py +0 -0
  69. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/signals.py +0 -0
  70. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/utils/__init__.py +0 -0
  71. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/utils/inspect.py +0 -0
  72. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/utils/naming.py +0 -0
  73. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/utils/pattern.py +0 -0
  74. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects/validators.py +0 -0
  75. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects.egg-info/SOURCES.txt +0 -0
  76. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects.egg-info/dependency_links.txt +0 -0
  77. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects.egg-info/entry_points.txt +0 -0
  78. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects.egg-info/requires.txt +0 -0
  79. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/sqlobjects.egg-info/top_level.txt +0 -0
  80. {sqlobjects-1.5.0 → sqlobjects-1.7.0}/tests/test_config.py +0 -0
@@ -1,3 +1,19 @@
1
+ ## 1.7.0 (2026-03-26)
2
+
3
+ ### Feat
4
+
5
+ - **metadata**: add foreignkey() constraint builder
6
+
7
+ ### Fix
8
+
9
+ - **raw**: allow SA expressions as arguments in raw() methods
10
+
11
+ ## 1.6.0 (2026-03-18)
12
+
13
+ ### Feat
14
+
15
+ - **metadata**: add foreignkey() constraint builder
16
+
1
17
  ## 1.5.0 (2026-03-16)
2
18
 
3
19
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.5.0
3
+ Version: 1.7.0
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.5.0"
3
+ version = "1.7.0"
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 = "MIT"
@@ -183,7 +183,9 @@ class FunctionExpression:
183
183
 
184
184
  Args:
185
185
  sql: Raw SQL function name or expression
186
- *args: Arguments to pass to the function. Use ... (Ellipsis) as placeholder for current expression
186
+ *args: Arguments to pass to the function. Use ... (Ellipsis) as placeholder for current expression.
187
+ SQLAlchemy expressions (ColumnElement, FunctionExpression) are passed through as-is;
188
+ plain Python values are wrapped with literal().
187
189
  **kwargs: Additional keyword arguments passed to the function
188
190
 
189
191
  Returns:
@@ -198,12 +200,17 @@ class FunctionExpression:
198
200
  User.age.avg().raw('CUSTOM_FUNCTION', 'param1', ..., 'param2')
199
201
  # Generates: CUSTOM_FUNCTION('param1', avg(age), 'param2')
200
202
 
201
- # Only current expression
202
- User.age.raw('CUSTOM_FUNCTION')
203
- # Generates: CUSTOM_FUNCTION(age)
203
+ # Passing another SA expression as argument
204
+ User.name.raw('CUSTOM_FUNCTION', other_func_expr)
205
+ # Generates: CUSTOM_FUNCTION(name, other_func_expr)
204
206
  """
205
207
  from sqlalchemy import literal
206
208
 
209
+ def _to_sql_arg(arg):
210
+ if isinstance(arg, (ColumnElement, FunctionExpression)):
211
+ return arg
212
+ return literal(arg)
213
+
207
214
  # Check if ... (Ellipsis) is used as placeholder
208
215
  if ... in args:
209
216
  # Replace ... with current expression
@@ -212,12 +219,10 @@ class FunctionExpression:
212
219
  if arg is ...:
213
220
  all_args.append(self.expression)
214
221
  else:
215
- all_args.append(literal(arg))
222
+ all_args.append(_to_sql_arg(arg))
216
223
  else:
217
224
  # Default behavior: current expression as first argument
218
- all_args = [self.expression]
219
- for arg in args:
220
- all_args.append(literal(arg))
225
+ all_args = [self.expression] + [_to_sql_arg(arg) for arg in args]
221
226
 
222
227
  # Use func to create the raw function call
223
228
  raw_func = getattr(_sa_func, sql)
@@ -425,6 +430,38 @@ class _FuncWrapper:
425
430
 
426
431
  def nth_value(self, col: Any, n: int) -> NthValueFunction: ...
427
432
 
433
+ def raw(self, sql: str, *args) -> FunctionExpression: ...
434
+
435
+ def raw(self, sql: str, *args) -> "FunctionExpression":
436
+ """Call an arbitrary SQL function by name.
437
+
438
+ Unlike col.raw() / FunctionExpression.raw() which insert the current
439
+ expression as the first argument, this standalone form takes the function
440
+ name and all arguments explicitly.
441
+
442
+ Args:
443
+ sql: SQL function name (e.g. "ts_rank", "to_tsvector")
444
+ *args: Arguments passed to the function. SQLAlchemy expressions
445
+ (ColumnElement, FunctionExpression) are used as-is; plain
446
+ Python values are wrapped with literal().
447
+
448
+ Examples:
449
+ func.raw("ts_rank", DocumentIndexes.content_vector, query_vec)
450
+ # → ts_rank(document_indexes.content_vector, <query_vec>)
451
+
452
+ func.raw("to_tsvector", "'simple'::regconfig", "some text")
453
+ # → to_tsvector('simple'::regconfig, 'some text')
454
+ """
455
+ from sqlalchemy import literal
456
+
457
+ def _to_sql_arg(arg):
458
+ if isinstance(arg, (ColumnElement, FunctionExpression)):
459
+ return arg
460
+ return literal(arg)
461
+
462
+ sa_func = getattr(_sa_func, sql)
463
+ return FunctionExpression(sa_func(*[_to_sql_arg(a) for a in args]))
464
+
428
465
  def __getattr__(self, name: str) -> Any:
429
466
  """Delegate to SQLAlchemy func for all other functions."""
430
467
  return getattr(_sa_func, name)
@@ -50,6 +50,12 @@ class ComparatorMixin:
50
50
 
51
51
  def raw(self, sql: str, *args, **kwargs) -> FunctionExpression:
52
52
  from sqlalchemy import literal
53
+ from sqlalchemy.sql.elements import ColumnElement
54
+
55
+ def _to_sql_arg(arg):
56
+ if isinstance(arg, (ColumnElement, FunctionExpression)):
57
+ return arg
58
+ return literal(arg)
53
59
 
54
60
  if ... in args:
55
61
  all_args = []
@@ -57,9 +63,9 @@ class ComparatorMixin:
57
63
  if arg is ...:
58
64
  all_args.append(self)
59
65
  else:
60
- all_args.append(literal(arg))
66
+ all_args.append(_to_sql_arg(arg))
61
67
  else:
62
- all_args = [self] + [literal(arg) for arg in args]
68
+ all_args = [self] + [_to_sql_arg(arg) for arg in args]
63
69
  raw_func = getattr(func, sql)
64
70
  return FunctionExpression(raw_func(*all_args, **kwargs))
65
71
 
@@ -22,6 +22,7 @@ __all__ = [
22
22
  "index",
23
23
  "constraint",
24
24
  "unique",
25
+ "foreignkey",
25
26
  ]
26
27
 
27
28
  _FIELD_NAME_PATTERN = re.compile(r"\b([a-zA-Z_][a-zA-Z0-9_]*)\b")
@@ -37,7 +38,7 @@ class _RawModelConfig:
37
38
  verbose_name_plural: str | None = None
38
39
  ordering: list[str] = field(default_factory=list)
39
40
  indexes: list[Index] = field(default_factory=list)
40
- constraints: list[CheckConstraint | UniqueConstraint] = field(default_factory=list)
41
+ constraints: list[CheckConstraint | UniqueConstraint | ForeignKeyConstraint] = field(default_factory=list)
41
42
  description: str | None = None
42
43
  db_options: dict[str, dict[str, Any]] = field(default_factory=dict)
43
44
  custom: dict[str, Any] = field(default_factory=dict)
@@ -52,7 +53,7 @@ class ModelConfig:
52
53
  verbose_name_plural: str
53
54
  ordering: list[str] = field(default_factory=list)
54
55
  indexes: list[Index] = field(default_factory=list)
55
- constraints: list[CheckConstraint | UniqueConstraint] = field(default_factory=list)
56
+ constraints: list[CheckConstraint | UniqueConstraint | ForeignKeyConstraint] = field(default_factory=list)
56
57
  description: str | None = None
57
58
  db_options: dict[str, dict[str, Any]] = field(default_factory=dict)
58
59
  custom: dict[str, Any] = field(default_factory=dict)
@@ -1109,3 +1110,55 @@ def unique(
1109
1110
  >>> unique("first_name", "last_name", name="uq_full_name")
1110
1111
  """
1111
1112
  return UniqueConstraint(*fields, name=name, **kwargs)
1113
+
1114
+
1115
+ def foreignkey(
1116
+ fields: str | list[str],
1117
+ references: str | list[str],
1118
+ *,
1119
+ name: str | None = None,
1120
+ ondelete: str | None = None,
1121
+ onupdate: str | None = None,
1122
+ deferrable: bool = False,
1123
+ initially: str = "IMMEDIATE",
1124
+ **kwargs: Any,
1125
+ ) -> ForeignKeyConstraint:
1126
+ """Create a ForeignKeyConstraint with convenient field name support.
1127
+
1128
+ Use this in Config.constraints for explicit constraint definition, custom
1129
+ names, or composite foreign keys. For simple single-column foreign keys,
1130
+ prefer the ``foreign_key()`` field descriptor instead.
1131
+
1132
+ Args:
1133
+ fields: Local field name(s) as string or list of strings.
1134
+ references: Referenced column(s) as "Table.column" or list thereof.
1135
+ Supports class names (e.g. "User.id") or table names (e.g. "users.id").
1136
+ name: Constraint name (optional, auto-generated if not provided).
1137
+ ondelete: Referential action on delete (CASCADE, SET NULL, RESTRICT, etc.).
1138
+ onupdate: Referential action on update.
1139
+ deferrable: Whether the constraint can be deferred.
1140
+ initially: Initial deferral state ("IMMEDIATE" or "DEFERRED").
1141
+ **kwargs: Additional SQLAlchemy ForeignKeyConstraint arguments.
1142
+
1143
+ Returns:
1144
+ SQLAlchemy ForeignKeyConstraint instance.
1145
+
1146
+ Examples:
1147
+ >>> foreignkey("author_id", "User.id")
1148
+ >>> foreignkey("author_id", "User.id", name="fk_posts_author", ondelete="CASCADE")
1149
+ >>> foreignkey(["a_id", "b_id"], ["A.id", "B.id"])
1150
+ """
1151
+ if isinstance(fields, str):
1152
+ fields = [fields]
1153
+ if isinstance(references, str):
1154
+ references = [references]
1155
+ return ForeignKeyConstraint(
1156
+ fields,
1157
+ references,
1158
+ name=name,
1159
+ ondelete=ondelete,
1160
+ onupdate=onupdate,
1161
+ deferrable=deferrable or None,
1162
+ initially=initially if deferrable else None,
1163
+ **kwargs,
1164
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.5.0
3
+ Version: 1.7.0
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>
File without changes
File without changes
File without changes