django-bulk-hooks 0.2.7__py3-none-any.whl → 0.2.8__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.

@@ -3,38 +3,38 @@ import logging
3
3
  logger = logging.getLogger(__name__)
4
4
 
5
5
 
6
- def resolve_dotted_attr(instance, dotted_path):
6
+ def resolve_field_path(instance, field_path):
7
7
  """
8
- Recursively resolve a dotted attribute path, e.g., "type.category".
8
+ Recursively resolve a field path using Django's __ notation, e.g., "author__profile__name".
9
9
 
10
10
  CRITICAL: For foreign key fields, uses attname to access the ID directly
11
11
  to avoid hooking Django's descriptor protocol which causes N+1 queries.
12
12
  """
13
- # For simple field access (no dots), use optimized field access
14
- if "." not in dotted_path:
13
+ # For simple field access (no __), use optimized field access
14
+ if "__" not in field_path:
15
15
  try:
16
16
  # Get the field from the model's meta to check if it's a foreign key
17
- field = instance._meta.get_field(dotted_path)
17
+ field = instance._meta.get_field(field_path)
18
18
  if field.is_relation and not field.many_to_many:
19
19
  # For foreign key fields, use attname to get the ID directly
20
20
  # This avoids hooking Django's descriptor protocol
21
21
  return getattr(instance, field.attname, None)
22
22
  else:
23
23
  # For regular fields, use normal getattr
24
- return getattr(instance, dotted_path, None)
24
+ return getattr(instance, field_path, None)
25
25
  except Exception:
26
26
  # If field lookup fails, fall back to normal getattr
27
- return getattr(instance, dotted_path, None)
27
+ return getattr(instance, field_path, None)
28
28
 
29
- # For dotted paths, traverse the relationship chain with FK optimization
29
+ # For paths with __, traverse the relationship chain with FK optimization
30
30
  current_instance = instance
31
- for i, attr in enumerate(dotted_path.split(".")):
31
+ for i, attr in enumerate(field_path.split("__")):
32
32
  if current_instance is None:
33
33
  return None
34
34
 
35
35
  try:
36
36
  # Check if this is the last attribute and if it's a FK field
37
- is_last_attr = i == len(dotted_path.split(".")) - 1
37
+ is_last_attr = i == len(field_path.split("__")) - 1
38
38
  if is_last_attr and hasattr(current_instance, "_meta"):
39
39
  try:
40
40
  field = current_instance._meta.get_field(attr)
@@ -79,11 +79,11 @@ class IsNotEqual(HookCondition):
79
79
  self.only_on_change = only_on_change
80
80
 
81
81
  def check(self, instance, original_instance=None):
82
- current = resolve_dotted_attr(instance, self.field)
82
+ current = resolve_field_path(instance, self.field)
83
83
  if self.only_on_change:
84
84
  if original_instance is None:
85
85
  return False
86
- previous = resolve_dotted_attr(original_instance, self.field)
86
+ previous = resolve_field_path(original_instance, self.field)
87
87
  return previous == self.value and current != self.value
88
88
  else:
89
89
  return current != self.value
@@ -96,12 +96,12 @@ class IsEqual(HookCondition):
96
96
  self.only_on_change = only_on_change
97
97
 
98
98
  def check(self, instance, original_instance=None):
99
- current = resolve_dotted_attr(instance, self.field)
99
+ current = resolve_field_path(instance, self.field)
100
100
 
101
101
  if self.only_on_change:
102
102
  if original_instance is None:
103
103
  return False
104
- previous = resolve_dotted_attr(original_instance, self.field)
104
+ previous = resolve_field_path(original_instance, self.field)
105
105
  return previous != self.value and current == self.value
106
106
  else:
107
107
  return current == self.value
@@ -116,8 +116,8 @@ class HasChanged(HookCondition):
116
116
  if not original_instance:
117
117
  return False
118
118
 
119
- current = resolve_dotted_attr(instance, self.field)
120
- previous = resolve_dotted_attr(original_instance, self.field)
119
+ current = resolve_field_path(instance, self.field)
120
+ previous = resolve_field_path(original_instance, self.field)
121
121
 
122
122
  return (current != previous) == self.has_changed
123
123
 
@@ -135,9 +135,9 @@ class WasEqual(HookCondition):
135
135
  def check(self, instance, original_instance=None):
136
136
  if original_instance is None:
137
137
  return False
138
- previous = resolve_dotted_attr(original_instance, self.field)
138
+ previous = resolve_field_path(original_instance, self.field)
139
139
  if self.only_on_change:
140
- current = resolve_dotted_attr(instance, self.field)
140
+ current = resolve_field_path(instance, self.field)
141
141
  return previous == self.value and current != self.value
142
142
  else:
143
143
  return previous == self.value
@@ -155,8 +155,8 @@ class ChangesTo(HookCondition):
155
155
  def check(self, instance, original_instance=None):
156
156
  if original_instance is None:
157
157
  return False
158
- previous = resolve_dotted_attr(original_instance, self.field)
159
- current = resolve_dotted_attr(instance, self.field)
158
+ previous = resolve_field_path(original_instance, self.field)
159
+ current = resolve_field_path(instance, self.field)
160
160
  return previous != self.value and current == self.value
161
161
 
162
162
 
@@ -166,7 +166,7 @@ class IsGreaterThan(HookCondition):
166
166
  self.value = value
167
167
 
168
168
  def check(self, instance, original_instance=None):
169
- current = resolve_dotted_attr(instance, self.field)
169
+ current = resolve_field_path(instance, self.field)
170
170
  return current is not None and current > self.value
171
171
 
172
172
 
@@ -176,7 +176,7 @@ class IsGreaterThanOrEqual(HookCondition):
176
176
  self.value = value
177
177
 
178
178
  def check(self, instance, original_instance=None):
179
- current = resolve_dotted_attr(instance, self.field)
179
+ current = resolve_field_path(instance, self.field)
180
180
  return current is not None and current >= self.value
181
181
 
182
182
 
@@ -186,7 +186,7 @@ class IsLessThan(HookCondition):
186
186
  self.value = value
187
187
 
188
188
  def check(self, instance, original_instance=None):
189
- current = resolve_dotted_attr(instance, self.field)
189
+ current = resolve_field_path(instance, self.field)
190
190
  return current is not None and current < self.value
191
191
 
192
192
 
@@ -196,7 +196,7 @@ class IsLessThanOrEqual(HookCondition):
196
196
  self.value = value
197
197
 
198
198
  def check(self, instance, original_instance=None):
199
- current = resolve_dotted_attr(instance, self.field)
199
+ current = resolve_field_path(instance, self.field)
200
200
  return current is not None and current <= self.value
201
201
 
202
202
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  License: MIT
6
6
  Keywords: django,bulk,hooks
@@ -1,6 +1,6 @@
1
1
  django_bulk_hooks/__init__.py,sha256=4QlWY5rqR9o2ddrNB-ypM4s1GtNJ1ZvL2ABhybaPpio,1823
2
2
  django_bulk_hooks/changeset.py,sha256=WALeiWDcjOBNdCKeidVKOPKAySKj9ZOvUJ-kWaVZYhM,7444
3
- django_bulk_hooks/conditions.py,sha256=tNnQZvcR-jOB8ZzpoVd03PVIy8rjiFdzmufD5mP7fTg,8107
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
6
  django_bulk_hooks/debug_utils.py,sha256=6T32E_Pms6gbCl94A55fJAe_ynFsK_CJBTaPcsG8tik,4578
@@ -20,7 +20,7 @@ django_bulk_hooks/operations/mti_handler.py,sha256=eIH-tImMqcWR5lLQr6Ca-HeVYta-U
20
20
  django_bulk_hooks/operations/mti_plans.py,sha256=fHUYbrUAHq8UXqxgAD43oHdTxOnEkmpxoOD4Qrzfqk8,2878
21
21
  django_bulk_hooks/queryset.py,sha256=ody4MXrRREL27Ts2ey1UpS0tb5Dxnw-6kN3unxPQ3zY,5860
22
22
  django_bulk_hooks/registry.py,sha256=UPerNhtVz_9tKZqrYSZD2LhjAcs4F6hVUuk8L5oOeHc,8821
23
- django_bulk_hooks-0.2.7.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
24
- django_bulk_hooks-0.2.7.dist-info/METADATA,sha256=4sY-WsqTUdlCIoNQBgnNpdScPMZ7BEbi980D4T4MsYE,9264
25
- django_bulk_hooks-0.2.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
- django_bulk_hooks-0.2.7.dist-info/RECORD,,
23
+ django_bulk_hooks-0.2.8.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
24
+ django_bulk_hooks-0.2.8.dist-info/METADATA,sha256=bkF722Ehasysowep-24uwfmkGPCvCcYuVz7v2-kawcM,9264
25
+ django_bulk_hooks-0.2.8.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
+ django_bulk_hooks-0.2.8.dist-info/RECORD,,