django-cte 1.3.3.dev20250526204410__tar.gz → 1.3.3.dev20250530125340__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.
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/PKG-INFO +3 -2
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/__init__.py +1 -1
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/cte.py +41 -3
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/expressions.py +2 -7
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/query.py +14 -42
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/pyproject.toml +4 -1
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/LICENSE +0 -0
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/README.md +0 -0
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/join.py +0 -0
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/meta.py +0 -0
- {django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/raw.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cte
|
|
3
|
-
Version: 1.3.3.
|
|
3
|
+
Version: 1.3.3.dev20250530125340
|
|
4
4
|
Summary: Common Table Expressions (CTE) for Django
|
|
5
5
|
Author-email: Daniel Miller <millerdev@gmail.com>
|
|
6
6
|
Requires-Python: >= 3.9
|
|
@@ -18,12 +18,13 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Framework :: Django
|
|
22
23
|
Classifier: Framework :: Django :: 4
|
|
23
24
|
Classifier: Framework :: Django :: 4.2
|
|
24
25
|
Classifier: Framework :: Django :: 5
|
|
25
|
-
Classifier: Framework :: Django :: 5.0
|
|
26
26
|
Classifier: Framework :: Django :: 5.1
|
|
27
|
+
Classifier: Framework :: Django :: 5.2
|
|
27
28
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
29
|
License-File: LICENSE
|
|
29
30
|
Requires-Dist: django
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from django.db.models import Manager
|
|
2
|
+
from django.db.models.expressions import Ref
|
|
2
3
|
from django.db.models.query import Q, QuerySet, ValuesIterable
|
|
3
4
|
from django.db.models.sql.datastructures import BaseTable
|
|
4
5
|
|
|
@@ -107,19 +108,27 @@ class With(object):
|
|
|
107
108
|
query.join(BaseTable(self.name, None))
|
|
108
109
|
query.default_cols = cte_query.default_cols
|
|
109
110
|
query.deferred_loading = cte_query.deferred_loading
|
|
111
|
+
if cte_query.values_select:
|
|
112
|
+
query.set_values(cte_query.values_select)
|
|
113
|
+
qs._iterable_class = ValuesIterable
|
|
114
|
+
for alias in getattr(cte_query, "selected", None) or ():
|
|
115
|
+
if alias not in cte_query.annotations:
|
|
116
|
+
field = cte_query.resolve_ref(alias).output_field
|
|
117
|
+
col = CTEColumnRef(alias, self.name, field)
|
|
118
|
+
query.add_annotation(col, alias)
|
|
110
119
|
if cte_query.annotations:
|
|
111
120
|
for alias, value in cte_query.annotations.items():
|
|
112
121
|
col = CTEColumnRef(alias, self.name, value.output_field)
|
|
113
122
|
query.add_annotation(col, alias)
|
|
114
|
-
if cte_query.values_select:
|
|
115
|
-
query.set_values(cte_query.values_select)
|
|
116
|
-
qs._iterable_class = ValuesIterable
|
|
117
123
|
query.annotation_select_mask = cte_query.annotation_select_mask
|
|
118
124
|
|
|
119
125
|
qs.query = query
|
|
120
126
|
return qs
|
|
121
127
|
|
|
122
128
|
def _resolve_ref(self, name):
|
|
129
|
+
selected = getattr(self.query, "selected", None)
|
|
130
|
+
if selected and name in selected and name not in self.query.annotations:
|
|
131
|
+
return Ref(name, self.query)
|
|
123
132
|
return self.query.resolve_ref(name)
|
|
124
133
|
|
|
125
134
|
|
|
@@ -153,6 +162,35 @@ class CTEQuerySet(QuerySet):
|
|
|
153
162
|
as_manager.queryset_only = True
|
|
154
163
|
as_manager = classmethod(as_manager)
|
|
155
164
|
|
|
165
|
+
def _combinator_query(self, *args, **kw):
|
|
166
|
+
clone = super()._combinator_query(*args, **kw)
|
|
167
|
+
if clone.query.combinator:
|
|
168
|
+
ctes = clone.query._with_ctes = []
|
|
169
|
+
seen = {}
|
|
170
|
+
for query in clone.query.combined_queries:
|
|
171
|
+
for cte in getattr(query, "_with_ctes", []):
|
|
172
|
+
if seen.get(cte.name) is cte:
|
|
173
|
+
continue
|
|
174
|
+
if cte.name in seen:
|
|
175
|
+
raise ValueError(
|
|
176
|
+
f"Found two or more CTEs named '{cte.name}'. "
|
|
177
|
+
"Hint: assign a unique name to each CTE."
|
|
178
|
+
)
|
|
179
|
+
ctes.append(cte)
|
|
180
|
+
seen[cte.name] = cte
|
|
181
|
+
if ctes:
|
|
182
|
+
def without_ctes(query):
|
|
183
|
+
if getattr(query, "_with_ctes", None):
|
|
184
|
+
query = query.clone()
|
|
185
|
+
query._with_ctes = []
|
|
186
|
+
return query
|
|
187
|
+
|
|
188
|
+
clone.query.combined_queries = [
|
|
189
|
+
without_ctes(query)
|
|
190
|
+
for query in clone.query.combined_queries
|
|
191
|
+
]
|
|
192
|
+
return clone
|
|
193
|
+
|
|
156
194
|
|
|
157
195
|
class CTEManager(Manager.from_queryset(CTEQuerySet)):
|
|
158
196
|
"""Manager for models that perform CTE queries"""
|
{django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/expressions.py
RENAMED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import django
|
|
2
1
|
from django.db.models import Subquery
|
|
3
2
|
|
|
4
3
|
|
|
@@ -31,12 +30,8 @@ class CTESubqueryResolver(object):
|
|
|
31
30
|
|
|
32
31
|
# --- end copied code --- #
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return clone.queryset.query
|
|
37
|
-
else:
|
|
38
|
-
def get_query(clone):
|
|
39
|
-
return clone.query
|
|
33
|
+
def get_query(clone):
|
|
34
|
+
return clone.query
|
|
40
35
|
|
|
41
36
|
# NOTE this uses the old (pre-Django 3) way of resolving.
|
|
42
37
|
# Should a different technique should be used on Django 3+?
|
{django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/query.py
RENAMED
|
@@ -8,7 +8,6 @@ from django.db.models.sql.compiler import (
|
|
|
8
8
|
SQLUpdateCompiler,
|
|
9
9
|
)
|
|
10
10
|
from django.db.models.sql.constants import LOUTER
|
|
11
|
-
from django.db.models.sql.where import ExtraWhere, WhereNode
|
|
12
11
|
|
|
13
12
|
from .expressions import CTESubqueryResolver
|
|
14
13
|
from .join import QJoin
|
|
@@ -55,13 +54,8 @@ class CTEQuery(Query):
|
|
|
55
54
|
clone._with_ctes = self._with_ctes[:]
|
|
56
55
|
return clone
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return self.__chain("clone", klass, *args, **kwargs)
|
|
61
|
-
|
|
62
|
-
else:
|
|
63
|
-
def chain(self, klass=None):
|
|
64
|
-
return self.__chain("chain", klass)
|
|
57
|
+
def chain(self, klass=None):
|
|
58
|
+
return self.__chain("chain", klass)
|
|
65
59
|
|
|
66
60
|
|
|
67
61
|
class CTECompiler(object):
|
|
@@ -82,49 +76,27 @@ class CTECompiler(object):
|
|
|
82
76
|
not isinstance(alias, QJoin) or alias.join_type != LOUTER
|
|
83
77
|
)
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
89
|
-
else:
|
|
90
|
-
compiler = cte.query.get_compiler(connection=connection)
|
|
79
|
+
compiler = cte.query.get_compiler(
|
|
80
|
+
connection=connection, elide_empty=should_elide_empty
|
|
81
|
+
)
|
|
91
82
|
|
|
92
83
|
qn = compiler.quote_name_unless_alias
|
|
93
84
|
try:
|
|
94
85
|
cte_sql, cte_params = compiler.as_sql()
|
|
95
86
|
except EmptyResultSet:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# only required for left outer joins, as standard inner
|
|
102
|
-
# joins should be optimized and raise the EmptyResultSet
|
|
103
|
-
query = cte.query.copy()
|
|
104
|
-
query.where = WhereNode([ExtraWhere(["1 = 0"], [])])
|
|
105
|
-
compiler = query.get_compiler(connection=connection)
|
|
106
|
-
cte_sql, cte_params = compiler.as_sql()
|
|
107
|
-
else:
|
|
108
|
-
# If the CTE raises an EmptyResultSet the SqlCompiler still
|
|
109
|
-
# needs to know the information about this base compiler
|
|
110
|
-
# like, col_count and klass_info.
|
|
111
|
-
as_sql()
|
|
112
|
-
raise
|
|
87
|
+
# If the CTE raises an EmptyResultSet the SqlCompiler still
|
|
88
|
+
# needs to know the information about this base compiler
|
|
89
|
+
# like, col_count and klass_info.
|
|
90
|
+
as_sql()
|
|
91
|
+
raise
|
|
113
92
|
template = cls.get_cte_query_template(cte)
|
|
114
93
|
ctes.append(template.format(name=qn(cte.name), query=cte_sql))
|
|
115
94
|
params.extend(cte_params)
|
|
116
95
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
explain_info = getattr(query, explain_attribute, None)
|
|
122
|
-
explain_format = getattr(explain_info, "format", None)
|
|
123
|
-
explain_options = getattr(explain_info, "options", {})
|
|
124
|
-
else:
|
|
125
|
-
explain_attribute = "explain_query"
|
|
126
|
-
explain_format = getattr(query, "explain_format", None)
|
|
127
|
-
explain_options = getattr(query, "explain_options", {})
|
|
96
|
+
explain_attribute = "explain_info"
|
|
97
|
+
explain_info = getattr(query, explain_attribute, None)
|
|
98
|
+
explain_format = getattr(explain_info, "format", None)
|
|
99
|
+
explain_options = getattr(explain_info, "options", {})
|
|
128
100
|
|
|
129
101
|
explain_query_or_info = getattr(query, explain_attribute, None)
|
|
130
102
|
sql = []
|
|
@@ -6,6 +6,8 @@ license = {file = "LICENSE"}
|
|
|
6
6
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
7
7
|
dynamic = ["version"]
|
|
8
8
|
requires-python = ">= 3.9"
|
|
9
|
+
# Python and Django versions are read from this file by GitHub Actions.
|
|
10
|
+
# Precise formatting is important.
|
|
9
11
|
classifiers = [
|
|
10
12
|
"Development Status :: 5 - Production/Stable",
|
|
11
13
|
'Environment :: Web Environment',
|
|
@@ -20,12 +22,13 @@ classifiers = [
|
|
|
20
22
|
'Programming Language :: Python :: 3.11',
|
|
21
23
|
'Programming Language :: Python :: 3.12',
|
|
22
24
|
'Programming Language :: Python :: 3.13',
|
|
25
|
+
'Programming Language :: Python :: 3.14',
|
|
23
26
|
'Framework :: Django',
|
|
24
27
|
'Framework :: Django :: 4',
|
|
25
28
|
'Framework :: Django :: 4.2',
|
|
26
29
|
'Framework :: Django :: 5',
|
|
27
|
-
'Framework :: Django :: 5.0',
|
|
28
30
|
'Framework :: Django :: 5.1',
|
|
31
|
+
'Framework :: Django :: 5.2',
|
|
29
32
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
30
33
|
]
|
|
31
34
|
dependencies = ["django"]
|
|
File without changes
|
|
File without changes
|
{django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/join.py
RENAMED
|
File without changes
|
{django_cte-1.3.3.dev20250526204410 → django_cte-1.3.3.dev20250530125340}/django_cte/meta.py
RENAMED
|
File without changes
|
|
File without changes
|