Просмотр исходного кода

atomically consume allocations and stock

master
Viggodevries 3 лет назад
Родитель
Сommit
18c87ef69b
1 измененных файлов: 63 добавлений и 28 удалений
  1. 63
    28
      src/oscar/apps/partner/abstract_models.py

+ 63
- 28
src/oscar/apps/partner/abstract_models.py Просмотреть файл

@@ -1,6 +1,6 @@
1 1
 from django.db import models, router
2
-from django.db.models import F, Value, signals
3
-from django.db.models.functions import Coalesce
2
+from django.db.models import F, signals
3
+from django.db.models.functions import Coalesce, Least
4 4
 from django.utils.functional import cached_property
5 5
 from django.utils.timezone import now
6 6
 from django.utils.translation import gettext_lazy as _
@@ -185,32 +185,22 @@ class AbstractStockRecord(models.Model):
185 185
         # Doesn't make sense to allocate if stock tracking is off.
186 186
         if not self.can_track_allocations:
187 187
             return
188
+
188 189
         # Send the pre-save signal
189
-        signals.pre_save.send(
190
-            sender=self.__class__,
191
-            instance=self,
192
-            created=False,
193
-            raw=False,
194
-            using=router.db_for_write(self.__class__, instance=self))
190
+        self.pre_save_signal()
195 191
 
196 192
         # Atomic update
197
-        (self.__class__.objects
198
-            .filter(pk=self.pk)
199
-            .update(num_allocated=(
200
-                Coalesce(F('num_allocated'), Value(0)) + quantity)))
193
+        (
194
+            self.__class__.objects.filter(pk=self.pk).update(
195
+                num_allocated=(Coalesce(F("num_allocated"), 0) + quantity)
196
+            )
197
+        )
201 198
 
202 199
         # Make sure the current object is up-to-date
203
-        if self.num_allocated is None:
204
-            self.num_allocated = 0
205
-        self.num_allocated += quantity
200
+        self.refresh_from_db(fields=["num_allocated"])
206 201
 
207 202
         # Send the post-save signal
208
-        signals.post_save.send(
209
-            sender=self.__class__,
210
-            instance=self,
211
-            created=False,
212
-            raw=False,
213
-            using=router.db_for_write(self.__class__, instance=self))
203
+        self.post_save_signal()
214 204
 
215 205
     allocate.alters_data = True
216 206
 
@@ -232,20 +222,65 @@ class AbstractStockRecord(models.Model):
232 222
         if not self.is_allocation_consumption_possible(quantity):
233 223
             raise InvalidStockAdjustment(
234 224
                 _('Invalid stock consumption request'))
235
-        self.num_allocated -= quantity
236
-        self.num_in_stock -= quantity
237
-        self.save()
225
+
226
+        # send the pre save signal
227
+        self.pre_save_signal()
228
+
229
+        # Atomically consume allocations and stock
230
+        (
231
+            self.__class__.objects.filter(pk=self.pk).update(
232
+                num_allocated=(Coalesce(F("num_allocated"), 0) - quantity),
233
+                num_in_stock=(Coalesce(F("num_in_stock"), 0) - quantity),
234
+            )
235
+        )
236
+
237
+        # Make sure current object is up-to-date
238
+        self.refresh_from_db(fields=["num_allocated", "num_in_stock"])
239
+
240
+        # Send the post-save signal
241
+        self.post_save_signal()
242
+
238 243
     consume_allocation.alters_data = True
239 244
 
240 245
     def cancel_allocation(self, quantity):
241 246
         if not self.can_track_allocations:
242 247
             return
243
-        # We ignore requests that request a cancellation of more than the
244
-        # amount already allocated.
245
-        self.num_allocated -= min(self.num_allocated, quantity)
246
-        self.save()
248
+
249
+        # send the pre save signal
250
+        self.pre_save_signal()
251
+
252
+        # Atomically consume allocations
253
+        (
254
+            self.__class__.objects.filter(pk=self.pk).update(
255
+                num_allocated=Coalesce(F("num_allocated"), 0)
256
+                - Least(Coalesce(F("num_allocated"), 0), quantity),
257
+            )
258
+        )
259
+
260
+        # Make sure current object is up-to-date
261
+        self.refresh_from_db(fields=["num_allocated"])
262
+
263
+        # Send the post-save signal
264
+        self.post_save_signal()
265
+
247 266
     cancel_allocation.alters_data = True
248 267
 
268
+    def pre_save_signal(self):
269
+        signals.pre_save.send(
270
+            sender=self.__class__,
271
+            instance=self,
272
+            created=False,
273
+            raw=False,
274
+            using=router.db_for_write(self.__class__, instance=self))
275
+
276
+    def post_save_signal(self):
277
+        signals.post_save.send(
278
+            sender=self.__class__,
279
+            instance=self,
280
+            created=False,
281
+            raw=False,
282
+            using=router.db_for_write(self.__class__, instance=self))
283
+
249 284
     @property
250 285
     def is_below_threshold(self):
251 286
         if self.low_stock_threshold is None:

Загрузка…
Отмена
Сохранить