django-bulk-hooks 0.2.10__py3-none-any.whl → 0.2.11__py3-none-any.whl
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.
Potentially problematic release.
This version of django-bulk-hooks might be problematic. Click here for more details.
- django_bulk_hooks/dispatcher.py +4 -0
- {django_bulk_hooks-0.2.10.dist-info → django_bulk_hooks-0.2.11.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.2.10.dist-info → django_bulk_hooks-0.2.11.dist-info}/RECORD +5 -6
- django_bulk_hooks/debug_utils.py +0 -145
- {django_bulk_hooks-0.2.10.dist-info → django_bulk_hooks-0.2.11.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.10.dist-info → django_bulk_hooks-0.2.11.dist-info}/WHEEL +0 -0
django_bulk_hooks/dispatcher.py
CHANGED
|
@@ -108,7 +108,9 @@ class HookDispatcher:
|
|
|
108
108
|
return
|
|
109
109
|
|
|
110
110
|
# Execute hooks in priority order
|
|
111
|
+
logger.info(f"🔥 HOOKS: Executing {len(hooks)} hooks for {changeset.model_cls.__name__}.{event}")
|
|
111
112
|
for handler_cls, method_name, condition, priority in hooks:
|
|
113
|
+
logger.info(f" → {handler_cls.__name__}.{method_name} (priority={priority})")
|
|
112
114
|
self._execute_hook(handler_cls, method_name, condition, changeset)
|
|
113
115
|
|
|
114
116
|
def _execute_hook(self, handler_cls, method_name, condition, changeset):
|
|
@@ -204,12 +206,14 @@ class HookDispatcher:
|
|
|
204
206
|
# New hooks that do use changeset: def hook(self, changeset, new_records, old_records, **kwargs)
|
|
205
207
|
#
|
|
206
208
|
# This is standard Python framework design (see Django signals, Flask hooks, etc.)
|
|
209
|
+
logger.info(f" 🚀 Executing: {handler_cls.__name__}.{method_name}")
|
|
207
210
|
try:
|
|
208
211
|
method(
|
|
209
212
|
changeset=filtered_changeset,
|
|
210
213
|
new_records=filtered_changeset.new_records,
|
|
211
214
|
old_records=filtered_changeset.old_records,
|
|
212
215
|
)
|
|
216
|
+
logger.info(f" ✅ Completed: {handler_cls.__name__}.{method_name}")
|
|
213
217
|
except Exception as e:
|
|
214
218
|
# Fail-fast: re-raise to rollback transaction
|
|
215
219
|
logger.error(
|
|
@@ -3,9 +3,8 @@ django_bulk_hooks/changeset.py,sha256=WALeiWDcjOBNdCKeidVKOPKAySKj9ZOvUJ-kWaVZYh
|
|
|
3
3
|
django_bulk_hooks/conditions.py,sha256=qtGjToKXC8FPUPK31Mib-GMzc9GSdrH90M2pT3CIsh8,8111
|
|
4
4
|
django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
|
|
5
5
|
django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
|
|
6
|
-
django_bulk_hooks/debug_utils.py,sha256=6T32E_Pms6gbCl94A55fJAe_ynFsK_CJBTaPcsG8tik,4578
|
|
7
6
|
django_bulk_hooks/decorators.py,sha256=hc8MSG5XXEiT5kgsf4Opzpj8jAb-OYqcvssuZxCIncQ,11894
|
|
8
|
-
django_bulk_hooks/dispatcher.py,sha256=
|
|
7
|
+
django_bulk_hooks/dispatcher.py,sha256=k3ndhY8e2N2nIHn22hFrCsDd3U5RVbPQtQ6Xf9E8UQE,8923
|
|
9
8
|
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
10
9
|
django_bulk_hooks/factory.py,sha256=JmjQiJPfAnytXrO6r6qOadX5yX0-sfpbZ9V8nwX3MAg,20013
|
|
11
10
|
django_bulk_hooks/handler.py,sha256=2-k0GPWGSQ6acfvV0qJgDH8aa0z51DqdpX5vSJ6Uawk,4759
|
|
@@ -20,7 +19,7 @@ django_bulk_hooks/operations/mti_handler.py,sha256=eIH-tImMqcWR5lLQr6Ca-HeVYta-U
|
|
|
20
19
|
django_bulk_hooks/operations/mti_plans.py,sha256=fHUYbrUAHq8UXqxgAD43oHdTxOnEkmpxoOD4Qrzfqk8,2878
|
|
21
20
|
django_bulk_hooks/queryset.py,sha256=ody4MXrRREL27Ts2ey1UpS0tb5Dxnw-6kN3unxPQ3zY,5860
|
|
22
21
|
django_bulk_hooks/registry.py,sha256=UPerNhtVz_9tKZqrYSZD2LhjAcs4F6hVUuk8L5oOeHc,8821
|
|
23
|
-
django_bulk_hooks-0.2.
|
|
24
|
-
django_bulk_hooks-0.2.
|
|
25
|
-
django_bulk_hooks-0.2.
|
|
26
|
-
django_bulk_hooks-0.2.
|
|
22
|
+
django_bulk_hooks-0.2.11.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
23
|
+
django_bulk_hooks-0.2.11.dist-info/METADATA,sha256=XjBb-_Q9gbnDEim8E7qQ4MY-iUUourgH8nNEkdFvHCI,9265
|
|
24
|
+
django_bulk_hooks-0.2.11.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
25
|
+
django_bulk_hooks-0.2.11.dist-info/RECORD,,
|
django_bulk_hooks/debug_utils.py
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Debug utilities for tracking N+1 queries and database performance.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
import time
|
|
7
|
-
from functools import wraps
|
|
8
|
-
from django.db import connection
|
|
9
|
-
from django.conf import settings
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def track_queries(func):
|
|
15
|
-
"""
|
|
16
|
-
Decorator to track database queries during function execution.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
@wraps(func)
|
|
20
|
-
def wrapper(*args, **kwargs):
|
|
21
|
-
# Reset query count
|
|
22
|
-
initial_queries = len(connection.queries)
|
|
23
|
-
initial_time = time.time()
|
|
24
|
-
|
|
25
|
-
logger.debug(
|
|
26
|
-
f"QUERY DEBUG: Starting {func.__name__} - initial query count: {initial_queries}"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
result = func(*args, **kwargs)
|
|
31
|
-
|
|
32
|
-
final_queries = len(connection.queries)
|
|
33
|
-
final_time = time.time()
|
|
34
|
-
query_count = final_queries - initial_queries
|
|
35
|
-
duration = final_time - initial_time
|
|
36
|
-
|
|
37
|
-
logger.debug(
|
|
38
|
-
f"QUERY DEBUG: Completed {func.__name__} - queries executed: {query_count}, duration: {duration:.4f}s"
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
# Log all queries executed during this function
|
|
42
|
-
if query_count > 0:
|
|
43
|
-
logger.debug(f"QUERY DEBUG: Queries executed in {func.__name__}:")
|
|
44
|
-
for i, query in enumerate(connection.queries[initial_queries:], 1):
|
|
45
|
-
logger.debug(
|
|
46
|
-
f"QUERY DEBUG: {i}. {query['sql'][:100]}... (time: {query['time']})"
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
return result
|
|
50
|
-
|
|
51
|
-
except Exception as e:
|
|
52
|
-
final_queries = len(connection.queries)
|
|
53
|
-
query_count = final_queries - initial_queries
|
|
54
|
-
logger.debug(
|
|
55
|
-
f"QUERY DEBUG: Exception in {func.__name__} - queries executed: {query_count}"
|
|
56
|
-
)
|
|
57
|
-
raise
|
|
58
|
-
|
|
59
|
-
return wrapper
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def log_query_count(context=""):
|
|
63
|
-
"""
|
|
64
|
-
Log the current query count with optional context.
|
|
65
|
-
"""
|
|
66
|
-
query_count = len(connection.queries)
|
|
67
|
-
logger.debug(f"QUERY DEBUG: Query count at {context}: {query_count}")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def log_recent_queries(count=5, context=""):
|
|
71
|
-
"""
|
|
72
|
-
Log the most recent database queries.
|
|
73
|
-
"""
|
|
74
|
-
recent_queries = connection.queries[-count:] if connection.queries else []
|
|
75
|
-
logger.debug(f"QUERY DEBUG: Recent {len(recent_queries)} queries at {context}:")
|
|
76
|
-
for i, query in enumerate(recent_queries, 1):
|
|
77
|
-
logger.debug(
|
|
78
|
-
f"QUERY DEBUG: {i}. {query['sql'][:100]}... (time: {query['time']})"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class QueryTracker:
|
|
83
|
-
"""
|
|
84
|
-
Context manager for tracking database queries.
|
|
85
|
-
"""
|
|
86
|
-
|
|
87
|
-
def __init__(self, context_name="QueryTracker"):
|
|
88
|
-
self.context_name = context_name
|
|
89
|
-
self.initial_queries = 0
|
|
90
|
-
self.start_time = 0
|
|
91
|
-
|
|
92
|
-
def __enter__(self):
|
|
93
|
-
self.initial_queries = len(connection.queries)
|
|
94
|
-
self.start_time = time.time()
|
|
95
|
-
logger.debug(
|
|
96
|
-
f"QUERY DEBUG: Starting {self.context_name} - initial query count: {self.initial_queries}"
|
|
97
|
-
)
|
|
98
|
-
return self
|
|
99
|
-
|
|
100
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
101
|
-
final_queries = len(connection.queries)
|
|
102
|
-
final_time = time.time()
|
|
103
|
-
query_count = final_queries - self.initial_queries
|
|
104
|
-
duration = final_time - self.start_time
|
|
105
|
-
|
|
106
|
-
logger.debug(
|
|
107
|
-
f"QUERY DEBUG: Completed {self.context_name} - queries executed: {query_count}, duration: {duration:.4f}s"
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
if query_count > 0:
|
|
111
|
-
logger.debug(f"QUERY DEBUG: Queries executed in {self.context_name}:")
|
|
112
|
-
for i, query in enumerate(connection.queries[self.initial_queries :], 1):
|
|
113
|
-
logger.debug(
|
|
114
|
-
f"QUERY DEBUG: {i}. {query['sql'][:100]}... (time: {query['time']})"
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
return False # Don't suppress exceptions
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def enable_django_query_logging():
|
|
121
|
-
"""
|
|
122
|
-
Enable Django's built-in query logging.
|
|
123
|
-
"""
|
|
124
|
-
if not settings.DEBUG:
|
|
125
|
-
logger.warning("Django query logging can only be enabled in DEBUG mode")
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
# Enable query logging
|
|
129
|
-
settings.LOGGING = {
|
|
130
|
-
"version": 1,
|
|
131
|
-
"disable_existing_loggers": False,
|
|
132
|
-
"handlers": {
|
|
133
|
-
"console": {
|
|
134
|
-
"class": "logging.StreamHandler",
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
"loggers": {
|
|
138
|
-
"django.db.backends": {
|
|
139
|
-
"level": "DEBUG",
|
|
140
|
-
"handlers": ["console"],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
logger.info("Django query logging enabled")
|
|
File without changes
|
|
File without changes
|