django-bulk-hooks 0.2.9__py3-none-any.whl → 0.2.93__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.
- django_bulk_hooks/__init__.py +20 -27
- django_bulk_hooks/changeset.py +214 -230
- django_bulk_hooks/conditions.py +12 -12
- django_bulk_hooks/decorators.py +68 -26
- django_bulk_hooks/dispatcher.py +369 -58
- django_bulk_hooks/factory.py +541 -565
- django_bulk_hooks/handler.py +106 -115
- django_bulk_hooks/helpers.py +258 -99
- django_bulk_hooks/manager.py +134 -130
- django_bulk_hooks/models.py +89 -76
- django_bulk_hooks/operations/__init__.py +5 -5
- django_bulk_hooks/operations/analyzer.py +299 -172
- django_bulk_hooks/operations/bulk_executor.py +742 -437
- django_bulk_hooks/operations/coordinator.py +928 -472
- django_bulk_hooks/operations/field_utils.py +335 -0
- django_bulk_hooks/operations/mti_handler.py +696 -473
- django_bulk_hooks/operations/mti_plans.py +103 -87
- django_bulk_hooks/operations/record_classifier.py +196 -0
- django_bulk_hooks/queryset.py +233 -189
- django_bulk_hooks/registry.py +276 -288
- {django_bulk_hooks-0.2.9.dist-info → django_bulk_hooks-0.2.93.dist-info}/METADATA +55 -4
- django_bulk_hooks-0.2.93.dist-info/RECORD +27 -0
- django_bulk_hooks/debug_utils.py +0 -145
- django_bulk_hooks-0.2.9.dist-info/RECORD +0 -26
- {django_bulk_hooks-0.2.9.dist-info → django_bulk_hooks-0.2.93.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.9.dist-info → django_bulk_hooks-0.2.93.dist-info}/WHEEL +0 -0
django_bulk_hooks/__init__.py
CHANGED
|
@@ -1,36 +1,29 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
+
from django_bulk_hooks.changeset import ChangeSet
|
|
4
|
+
from django_bulk_hooks.changeset import RecordChange
|
|
5
|
+
from django_bulk_hooks.constants import DEFAULT_BULK_UPDATE_BATCH_SIZE
|
|
6
|
+
from django_bulk_hooks.dispatcher import HookDispatcher
|
|
7
|
+
from django_bulk_hooks.dispatcher import get_dispatcher
|
|
8
|
+
from django_bulk_hooks.factory import clear_hook_factories
|
|
9
|
+
from django_bulk_hooks.factory import configure_hook_container
|
|
10
|
+
from django_bulk_hooks.factory import configure_nested_container
|
|
11
|
+
from django_bulk_hooks.factory import create_hook_instance
|
|
12
|
+
from django_bulk_hooks.factory import is_container_configured
|
|
13
|
+
from django_bulk_hooks.factory import set_default_hook_factory
|
|
14
|
+
from django_bulk_hooks.factory import set_hook_factory
|
|
3
15
|
from django_bulk_hooks.handler import Hook as HookClass
|
|
16
|
+
from django_bulk_hooks.helpers import build_changeset_for_create
|
|
17
|
+
from django_bulk_hooks.helpers import build_changeset_for_delete
|
|
18
|
+
from django_bulk_hooks.helpers import build_changeset_for_update
|
|
19
|
+
from django_bulk_hooks.helpers import dispatch_hooks_for_operation
|
|
4
20
|
from django_bulk_hooks.manager import BulkHookManager
|
|
5
|
-
from django_bulk_hooks.
|
|
6
|
-
set_hook_factory,
|
|
7
|
-
set_default_hook_factory,
|
|
8
|
-
configure_hook_container,
|
|
9
|
-
configure_nested_container,
|
|
10
|
-
clear_hook_factories,
|
|
11
|
-
create_hook_instance,
|
|
12
|
-
is_container_configured,
|
|
13
|
-
)
|
|
14
|
-
from django_bulk_hooks.constants import DEFAULT_BULK_UPDATE_BATCH_SIZE
|
|
15
|
-
from django_bulk_hooks.changeset import ChangeSet, RecordChange
|
|
16
|
-
from django_bulk_hooks.dispatcher import get_dispatcher, HookDispatcher
|
|
17
|
-
from django_bulk_hooks.helpers import (
|
|
18
|
-
build_changeset_for_create,
|
|
19
|
-
build_changeset_for_update,
|
|
20
|
-
build_changeset_for_delete,
|
|
21
|
-
dispatch_hooks_for_operation,
|
|
22
|
-
)
|
|
21
|
+
from django_bulk_hooks.operations import BulkExecutor
|
|
23
22
|
|
|
24
23
|
# Service layer (NEW architecture)
|
|
25
|
-
from django_bulk_hooks.operations import
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
BulkExecutor,
|
|
29
|
-
MTIHandler,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
# Add NullHandler to prevent logging messages if the application doesn't configure logging
|
|
33
|
-
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
24
|
+
from django_bulk_hooks.operations import BulkOperationCoordinator
|
|
25
|
+
from django_bulk_hooks.operations import ModelAnalyzer
|
|
26
|
+
from django_bulk_hooks.operations import MTIHandler
|
|
34
27
|
|
|
35
28
|
__all__ = [
|
|
36
29
|
"BulkHookManager",
|
django_bulk_hooks/changeset.py
CHANGED
|
@@ -1,230 +1,214 @@
|
|
|
1
|
-
"""
|
|
2
|
-
ChangeSet and RecordChange classes for Salesforce-style hook context.
|
|
3
|
-
|
|
4
|
-
Provides a first-class abstraction for tracking changes in bulk operations,
|
|
5
|
-
similar to Salesforce's Hook.new, Hook.old, and Hook.newMap.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class RecordChange:
|
|
10
|
-
"""
|
|
11
|
-
Represents a single record change with old/new state.
|
|
12
|
-
|
|
13
|
-
Similar to accessing Hook.newMap.get(id) in Salesforce, but with
|
|
14
|
-
additional conveniences like O(1) field change detection.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
def __init__(self, new_record, old_record=None, changed_fields=None):
|
|
18
|
-
"""
|
|
19
|
-
Initialize a RecordChange.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
new_record: The new/current state of the record
|
|
23
|
-
old_record: The old/previous state of the record (None for creates)
|
|
24
|
-
changed_fields: Optional pre-computed set of changed field names.
|
|
25
|
-
If None, will be computed lazily on first access.
|
|
26
|
-
"""
|
|
27
|
-
self.new_record = new_record
|
|
28
|
-
self.old_record = old_record
|
|
29
|
-
self._changed_fields = changed_fields
|
|
30
|
-
self._pk = getattr(new_record, "pk", None) if new_record else None
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def pk(self):
|
|
34
|
-
"""Primary key of the record."""
|
|
35
|
-
return self._pk
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def changed_fields(self):
|
|
39
|
-
"""
|
|
40
|
-
Set of field names that have changed.
|
|
41
|
-
|
|
42
|
-
Computed lazily on first access and cached for O(1) subsequent checks.
|
|
43
|
-
"""
|
|
44
|
-
if self._changed_fields is None:
|
|
45
|
-
self._changed_fields = self._compute_changed_fields()
|
|
46
|
-
return self._changed_fields
|
|
47
|
-
|
|
48
|
-
def has_changed(self, field_name):
|
|
49
|
-
"""
|
|
50
|
-
O(1) check if a specific field has changed.
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
field_name: Name of the field to check
|
|
54
|
-
|
|
55
|
-
Returns:
|
|
56
|
-
True if the field value changed, False otherwise
|
|
57
|
-
"""
|
|
58
|
-
return field_name in self.changed_fields
|
|
59
|
-
|
|
60
|
-
def get_old_value(self, field_name):
|
|
61
|
-
"""
|
|
62
|
-
Get the old value for a field.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
field_name: Name of the field
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
The old value, or None if no old record exists
|
|
69
|
-
"""
|
|
70
|
-
if self.old_record is None:
|
|
71
|
-
return None
|
|
72
|
-
return getattr(self.old_record, field_name, None)
|
|
73
|
-
|
|
74
|
-
def get_new_value(self, field_name):
|
|
75
|
-
"""
|
|
76
|
-
Get the new value for a field.
|
|
77
|
-
|
|
78
|
-
Args:
|
|
79
|
-
field_name: Name of the field
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
The new value
|
|
83
|
-
"""
|
|
84
|
-
return getattr(self.new_record, field_name, None)
|
|
85
|
-
|
|
86
|
-
def _compute_changed_fields(self):
|
|
87
|
-
"""
|
|
88
|
-
Compute which fields have changed between old and new records.
|
|
89
|
-
|
|
90
|
-
Uses Django's field.get_prep_value() for proper comparison that
|
|
91
|
-
handles database-level transformations.
|
|
92
|
-
|
|
93
|
-
Returns:
|
|
94
|
-
Set of field names that have changed
|
|
95
|
-
"""
|
|
96
|
-
if self.old_record is None:
|
|
97
|
-
return set()
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
"""
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
Split ChangeSet into smaller chunks for memory-efficient processing.
|
|
216
|
-
|
|
217
|
-
Useful for processing very large bulk operations without loading
|
|
218
|
-
all data into memory at once.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
chunk_size: Number of changes per chunk
|
|
222
|
-
|
|
223
|
-
Yields:
|
|
224
|
-
ChangeSet instances, each with up to chunk_size changes
|
|
225
|
-
"""
|
|
226
|
-
for i in range(0, len(self.changes), chunk_size):
|
|
227
|
-
chunk_changes = self.changes[i : i + chunk_size]
|
|
228
|
-
yield ChangeSet(
|
|
229
|
-
self.model_cls, chunk_changes, self.operation_type, self.operation_meta
|
|
230
|
-
)
|
|
1
|
+
"""
|
|
2
|
+
ChangeSet and RecordChange classes for Salesforce-style hook context.
|
|
3
|
+
|
|
4
|
+
Provides a first-class abstraction for tracking changes in bulk operations,
|
|
5
|
+
similar to Salesforce's Hook.new, Hook.old, and Hook.newMap.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RecordChange:
|
|
10
|
+
"""
|
|
11
|
+
Represents a single record change with old/new state.
|
|
12
|
+
|
|
13
|
+
Similar to accessing Hook.newMap.get(id) in Salesforce, but with
|
|
14
|
+
additional conveniences like O(1) field change detection.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, new_record, old_record=None, changed_fields=None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize a RecordChange.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
new_record: The new/current state of the record
|
|
23
|
+
old_record: The old/previous state of the record (None for creates)
|
|
24
|
+
changed_fields: Optional pre-computed set of changed field names.
|
|
25
|
+
If None, will be computed lazily on first access.
|
|
26
|
+
"""
|
|
27
|
+
self.new_record = new_record
|
|
28
|
+
self.old_record = old_record
|
|
29
|
+
self._changed_fields = changed_fields
|
|
30
|
+
self._pk = getattr(new_record, "pk", None) if new_record else None
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def pk(self):
|
|
34
|
+
"""Primary key of the record."""
|
|
35
|
+
return self._pk
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def changed_fields(self):
|
|
39
|
+
"""
|
|
40
|
+
Set of field names that have changed.
|
|
41
|
+
|
|
42
|
+
Computed lazily on first access and cached for O(1) subsequent checks.
|
|
43
|
+
"""
|
|
44
|
+
if self._changed_fields is None:
|
|
45
|
+
self._changed_fields = self._compute_changed_fields()
|
|
46
|
+
return self._changed_fields
|
|
47
|
+
|
|
48
|
+
def has_changed(self, field_name):
|
|
49
|
+
"""
|
|
50
|
+
O(1) check if a specific field has changed.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
field_name: Name of the field to check
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if the field value changed, False otherwise
|
|
57
|
+
"""
|
|
58
|
+
return field_name in self.changed_fields
|
|
59
|
+
|
|
60
|
+
def get_old_value(self, field_name):
|
|
61
|
+
"""
|
|
62
|
+
Get the old value for a field.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
field_name: Name of the field
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The old value, or None if no old record exists
|
|
69
|
+
"""
|
|
70
|
+
if self.old_record is None:
|
|
71
|
+
return None
|
|
72
|
+
return getattr(self.old_record, field_name, None)
|
|
73
|
+
|
|
74
|
+
def get_new_value(self, field_name):
|
|
75
|
+
"""
|
|
76
|
+
Get the new value for a field.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
field_name: Name of the field
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The new value
|
|
83
|
+
"""
|
|
84
|
+
return getattr(self.new_record, field_name, None)
|
|
85
|
+
|
|
86
|
+
def _compute_changed_fields(self):
|
|
87
|
+
"""
|
|
88
|
+
Compute which fields have changed between old and new records.
|
|
89
|
+
|
|
90
|
+
Uses Django's field.get_prep_value() for proper comparison that
|
|
91
|
+
handles database-level transformations.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Set of field names that have changed
|
|
95
|
+
"""
|
|
96
|
+
if self.old_record is None:
|
|
97
|
+
return set()
|
|
98
|
+
|
|
99
|
+
# Import here to avoid circular dependency
|
|
100
|
+
from .operations.field_utils import get_changed_fields
|
|
101
|
+
|
|
102
|
+
model_cls = self.new_record.__class__
|
|
103
|
+
return get_changed_fields(self.old_record, self.new_record, model_cls)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ChangeSet:
|
|
107
|
+
"""
|
|
108
|
+
Collection of RecordChanges for a bulk operation.
|
|
109
|
+
|
|
110
|
+
Similar to Salesforce's Hook context (Hook.new, Hook.old, Hook.newMap),
|
|
111
|
+
but enhanced for Python's bulk operations paradigm with O(1) lookups and
|
|
112
|
+
additional metadata.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __init__(self, model_cls, changes, operation_type, operation_meta=None):
|
|
116
|
+
"""
|
|
117
|
+
Initialize a ChangeSet.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
model_cls: The Django model class
|
|
121
|
+
changes: List of RecordChange instances
|
|
122
|
+
operation_type: Type of operation ('create', 'update', 'delete')
|
|
123
|
+
operation_meta: Optional dict of additional metadata (e.g., update_kwargs)
|
|
124
|
+
"""
|
|
125
|
+
self.model_cls = model_cls
|
|
126
|
+
self.changes = changes # List[RecordChange]
|
|
127
|
+
self.operation_type = operation_type
|
|
128
|
+
self.operation_meta = operation_meta or {}
|
|
129
|
+
|
|
130
|
+
# Build PK -> RecordChange map for O(1) lookups (like Hook.newMap)
|
|
131
|
+
self._pk_to_change = {c.pk: c for c in changes if c.pk is not None}
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def new_records(self):
|
|
135
|
+
"""
|
|
136
|
+
List of new/current record states.
|
|
137
|
+
|
|
138
|
+
Similar to Hook.new in Salesforce.
|
|
139
|
+
"""
|
|
140
|
+
return [c.new_record for c in self.changes if c.new_record is not None]
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def old_records(self):
|
|
144
|
+
"""
|
|
145
|
+
List of old/previous record states.
|
|
146
|
+
|
|
147
|
+
Similar to Hook.old in Salesforce.
|
|
148
|
+
Only includes records that have old states (excludes creates).
|
|
149
|
+
"""
|
|
150
|
+
return [c.old_record for c in self.changes if c.old_record is not None]
|
|
151
|
+
|
|
152
|
+
def has_field_changed(self, pk, field_name):
|
|
153
|
+
"""
|
|
154
|
+
O(1) check if a field changed for a specific record.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
pk: Primary key of the record
|
|
158
|
+
field_name: Name of the field to check
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
True if the field changed, False otherwise
|
|
162
|
+
"""
|
|
163
|
+
change = self._pk_to_change.get(pk)
|
|
164
|
+
return change.has_changed(field_name) if change else False
|
|
165
|
+
|
|
166
|
+
def get_old_value(self, pk, field_name):
|
|
167
|
+
"""
|
|
168
|
+
Get the old value for a specific record and field.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
pk: Primary key of the record
|
|
172
|
+
field_name: Name of the field
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
The old value, or None if not found
|
|
176
|
+
"""
|
|
177
|
+
change = self._pk_to_change.get(pk)
|
|
178
|
+
return change.get_old_value(field_name) if change else None
|
|
179
|
+
|
|
180
|
+
def get_new_value(self, pk, field_name):
|
|
181
|
+
"""
|
|
182
|
+
Get the new value for a specific record and field.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
pk: Primary key of the record
|
|
186
|
+
field_name: Name of the field
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The new value, or None if not found
|
|
190
|
+
"""
|
|
191
|
+
change = self._pk_to_change.get(pk)
|
|
192
|
+
return change.get_new_value(field_name) if change else None
|
|
193
|
+
|
|
194
|
+
def chunk(self, chunk_size):
|
|
195
|
+
"""
|
|
196
|
+
Split ChangeSet into smaller chunks for memory-efficient processing.
|
|
197
|
+
|
|
198
|
+
Useful for processing very large bulk operations without loading
|
|
199
|
+
all data into memory at once.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
chunk_size: Number of changes per chunk
|
|
203
|
+
|
|
204
|
+
Yields:
|
|
205
|
+
ChangeSet instances, each with up to chunk_size changes
|
|
206
|
+
"""
|
|
207
|
+
for i in range(0, len(self.changes), chunk_size):
|
|
208
|
+
chunk_changes = self.changes[i : i + chunk_size]
|
|
209
|
+
yield ChangeSet(
|
|
210
|
+
self.model_cls,
|
|
211
|
+
chunk_changes,
|
|
212
|
+
self.operation_type,
|
|
213
|
+
self.operation_meta,
|
|
214
|
+
)
|
django_bulk_hooks/conditions.py
CHANGED
|
@@ -19,9 +19,8 @@ def resolve_field_path(instance, field_path):
|
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
return getattr(instance, field_path, None)
|
|
22
|
+
# For regular fields, use normal getattr
|
|
23
|
+
return getattr(instance, field_path, None)
|
|
25
24
|
except Exception:
|
|
26
25
|
# If field lookup fails, fall back to normal getattr
|
|
27
26
|
return getattr(instance, field_path, None)
|
|
@@ -41,7 +40,9 @@ def resolve_field_path(instance, field_path):
|
|
|
41
40
|
if field.is_relation and not field.many_to_many:
|
|
42
41
|
# Use attname for the final FK field access
|
|
43
42
|
current_instance = getattr(
|
|
44
|
-
current_instance,
|
|
43
|
+
current_instance,
|
|
44
|
+
field.attname,
|
|
45
|
+
None,
|
|
45
46
|
)
|
|
46
47
|
continue
|
|
47
48
|
except:
|
|
@@ -85,8 +86,7 @@ class IsNotEqual(HookCondition):
|
|
|
85
86
|
return False
|
|
86
87
|
previous = resolve_field_path(original_instance, self.field)
|
|
87
88
|
return previous == self.value and current != self.value
|
|
88
|
-
|
|
89
|
-
return current != self.value
|
|
89
|
+
return current != self.value
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
class IsEqual(HookCondition):
|
|
@@ -103,8 +103,7 @@ class IsEqual(HookCondition):
|
|
|
103
103
|
return False
|
|
104
104
|
previous = resolve_field_path(original_instance, self.field)
|
|
105
105
|
return previous != self.value and current == self.value
|
|
106
|
-
|
|
107
|
-
return current == self.value
|
|
106
|
+
return current == self.value
|
|
108
107
|
|
|
109
108
|
|
|
110
109
|
class HasChanged(HookCondition):
|
|
@@ -139,8 +138,7 @@ class WasEqual(HookCondition):
|
|
|
139
138
|
if self.only_on_change:
|
|
140
139
|
current = resolve_field_path(instance, self.field)
|
|
141
140
|
return previous == self.value and current != self.value
|
|
142
|
-
|
|
143
|
-
return previous == self.value
|
|
141
|
+
return previous == self.value
|
|
144
142
|
|
|
145
143
|
|
|
146
144
|
class ChangesTo(HookCondition):
|
|
@@ -207,7 +205,8 @@ class AndCondition(HookCondition):
|
|
|
207
205
|
|
|
208
206
|
def check(self, instance, original_instance=None):
|
|
209
207
|
return self.cond1.check(instance, original_instance) and self.cond2.check(
|
|
210
|
-
instance,
|
|
208
|
+
instance,
|
|
209
|
+
original_instance,
|
|
211
210
|
)
|
|
212
211
|
|
|
213
212
|
|
|
@@ -218,7 +217,8 @@ class OrCondition(HookCondition):
|
|
|
218
217
|
|
|
219
218
|
def check(self, instance, original_instance=None):
|
|
220
219
|
return self.cond1.check(instance, original_instance) or self.cond2.check(
|
|
221
|
-
instance,
|
|
220
|
+
instance,
|
|
221
|
+
original_instance,
|
|
222
222
|
)
|
|
223
223
|
|
|
224
224
|
|