У меня есть следующие модели:
class InventoryAction(CustomModel):
action_content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT,
limit_choices_to={'model__in': ('inventoryinput', 'inventorytransfer', 'inventoryadjustment', 'physicalinventory', 'requisition', 'sale')}, related_name='inventory_actions', verbose_name=_("Tipo de Acción"))
action_object_id = models.PositiveIntegerField(verbose_name=_("ID de la acción"))
action_object = GenericForeignKey('action_content_type', 'action_object_id')
timestamp = models.DateTimeField(auto_now=True, verbose_name=_("Fecha y hora"))
class Meta:
verbose_name = _("Acción de Inventario")
verbose_name_plural = _("Acciones de Inventario")
def __str__(self):
return "{}-{}/{}/{}".format(self.id, self.action_content_type.model, self.action_object_id, self.timestamp)
class InventoryActionProduct(CustomModel):
inventory_action = models.ForeignKey(InventoryAction, on_delete=models.PROTECT, related_name='products', verbose_name=_("Acción de Inventario"))
product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name='actions', verbose_name=_("Producto"))
amount = models.FloatField(verbose_name=_("Cantidad"))
class Meta:
verbose_name = _("Producto de Acción de Inventario")
verbose_name_plural = _("Productos de Acciones de Inventario")
def __str__(self):
return "[{}] {}/{}/{}".format(self.id, self.inventory_action.action_content_type.model, self.product.name, self.inventory_action.timestamp)
class InventoryActionItem(CustomModel):
inventory_action_product = models.ForeignKey(InventoryActionProduct, on_delete=models.PROTECT, related_name='items', verbose_name=_("Producto de Acción de Inventario"))
product_item = models.ForeignKey(ProductItem, on_delete=models.PROTECT, related_name='invetory_action', verbose_name=_("Artículo"))
class Meta:
verbose_name = _("Artículo de Acción de Inventario")
verbose_name_plural = _("Artícuos de Acciones de Inventario")
def __str__(self):
return "[{}] {}/{}".format(self.id, self.inventory_action_product.product.name, self.product_item.serial_number)
class InventoryInput(CustomModel):
repository = models.ForeignKey(Repository, on_delete=models.PROTECT, related_name='inputs', verbose_name=_("Almacén"))
class Meta:
verbose_name = _("Entrada de Inventario")
verbose_name_plural = _("Entradas de Inventario")
def __str__(self):
return "{}-{}".format(self.id, self.repository)
KARDEX_INPUT = 'INPUT'
KARDEX_OUTPUT = 'OUTPUT'
KARDEX_CHOICES = (
(KARDEX_INPUT, _("Entrada")),
(KARDEX_OUTPUT, _("Salida")),
)
class Kardex(CustomModel):
type = models.CharField(max_length=6, choices=KARDEX_CHOICES, verbose_name=_("Tipo de Movimiento"))
repository = models.ForeignKey(Repository, on_delete=models.PROTECT, related_name='kardex', verbose_name=_("Almacén"))
product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name='kardex', verbose_name=_("Producto"))
amount = models.FloatField(verbose_name=_("Cantidad"))
stock = models.FloatField(null=True, blank=True, verbose_name=_("Existencia"))
timestamp = models.DateTimeField(auto_now=True, verbose_name=_("Fecha y Hora"))
reference = models.ForeignKey(InventoryActionProduct, null=True, blank=True, on_delete=models.PROTECT, related_name='kardex', verbose_name=_("Referencie"))
class Meta:
verbose_name = _("Cardex")
verbose_name_plural = _("Cardex")
def __str__(self):
return "{}-{}/{}/{}/{}/{}".format(self.id, self.type, self.repository.name, self.product.name, self.amount, self.timestamp)
def save(self, *args, **kwargs):
last_kardex_entry = Kardex.objects.filter(repository=self.repository, product=self.product).order_by('timestamp').last()
if not last_kardex_entry:
last_kardex_stock = 0
else:
last_kardex_stock = last_kardex_entry.stock
if self.type == KARDEX_OUTPUT:
if last_kardex_stock < self.amount:
raise CustomValidation(_("La cantidad existente del producto [{}]{} en la tienda [{}]{} es de {}, y se están intentando vender {}". format(self.product.id, self.product.name, self.repository.store.id, self.repository.store.name, last_kardex_stock, self.amount)), 'amount', status.HTTP_400_BAD_REQUEST)
self.stock = last_kardex_stock - self.amount
else:
self.stock = last_kardex_stock + self.amount
super().save(*args, **kwargs)
И у меня есть следующее представление:
class InventoryExecuteActionViewSet(viewsets.ViewSet):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
action_type = request.data['action']
try:
self.repository = request.data['repository']['id']
except:
raise CustomValidation(_("Debe proporcionar el id del almacén"), 'repository', status.HTTP_400_BAD_REQUEST)
try:
self.repository = models.Repository.objects.get(id=self.repository)
except ObjectDoesNotExist:
raise CustomValidation(_("No existe un almacén con id [{}]".format(self.repository)), 'repository', status.HTTP_400_BAD_REQUEST)
action_controller = get_action_controller(action_type)
result = action_controller(request.data)
result_to_return = result.data
return Response(result_to_return)
У меня также есть следующие классы в другом файле (utils.py
):
class InventoryActionController:
def __init__(self, data=None):
from inventory.models import InventoryAction
self.data = {}
self.inventory_action = InventoryAction()
self.inventory_action_products = []
self.inventory_action_items = []
self.action = None
self.origin_data = data
self.save_data()
def save_data(self):
self.save_action()
self.save_inventory_action()
self.save_action_products()
def save_action(self):
pass
def save_inventory_action(self):
self.inventory_action.action_content_type = ContentType.objects.get(model=self.action.__class__.__name__.lower())
self.inventory_action.action_object_id = self.action.id
self.inventory_action.timestamp = datetime.today()
self.inventory_action.save()
def save_action_products(self):
for action_product in self.origin_data['entries']:
new_action_product = self.save_action_product(action_product)
self.inventory_action_products.append(new_action_product)
def save_action_product(self, action_product):
from inventory.models import InventoryActionProduct
from inventory.serializers import InventoryActionProductSerializer, InventoryActionItemSerializer
new_action_product = InventoryActionProduct()
new_action_product.inventory_action = self.inventory_action
new_action_product.product_id = action_product['product']['id']
new_action_product.amount = action_product.get('amountCurr', None)
new_action_product.save()
new_action_product_data = InventoryActionProductSerializer(new_action_product).data
if new_action_product.product.is_seriable:
self.save_product_items(new_action_product, action_product['items'])
new_action_product_data['items'] = InventoryActionItemSerializer(new_action_product.items, many=True).data
self.data['products'].append(new_action_product_data)
self.save_kardex(new_action_product)
return new_action_product
def save_product_items(self, action_product, action_items):
from inventory.models import InventoryActionItem
new_action_items = []
for action_item in action_items:
new_product_item = self.save_product_item(action_product.product, action_item)
new_action_item = InventoryActionItem()
new_action_item.action_product = action_product
new_action_item.product_item = new_product_item
new_action_item.save()
return new_action_items
def save_product_item(self, product, item):
from inventory.models import ProductItem
new_product_item = ProductItem()
new_product_item.product = product
new_product_item.serial_number = item
return new_product_item
def save_kardex(self, action_product):
pass
class InventoryInputController(InventoryActionController):
def save_action(self):
from inventory.models import InventoryInput
from inventory.serializers import InventoryInputSerializer
self.action = InventoryInput()
self.action.repository_id = self.origin_data['repository']['id']
self.action.save()
self.data = InventoryInputSerializer(self.action).data
self.data['products'] = []
self.save_inventory_action()
self.save_action_products()
def save_kardex(self, action_product):
from inventory.models import KARDEX_INPUT
KardexController().save(KARDEX_INPUT, action_product)
class KardexController:
def save(self, action_type, action_product):
from inventory.models import Kardex
new_kardex_entry = Kardex()
new_kardex_entry.type = action_type
new_kardex_entry.repository_id = action_product.inventory_action.action_object.repository_id
new_kardex_entry.product_id = action_product.product_id
new_kardex_entry.amount = action_product.amount
new_kardex_entry.reference = action_product
new_kardex_entry.save()
return new_kardex_entry
Я установил базу данных для транзакций atomi c:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'database_name',
'USER': 'postgres',
'PASSWORD': 'secret',
'HOST': 'localhost',
'PORT': '',
'ATOMIC_REQUEST': True,
}
}
Я ожидал, что, когда запрос завершится с исключением, все транзакции базы данных будут откатываться, но я вижу, что в модели InventoryAction
и Kardex
есть несколько записей, даже когда только один запрос заканчивался return Response()
, а все остальные заканчивались исключением.
Что я недоразумение об атомах c Транзакции?