繁体   English   中英

Django 不会通过原子事务回滚

[英]Django is not rolling back with atomic transactions

我有以下型号:

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

我已经为原子事务设置了数据库:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'database_name',
        'USER': 'postgres',
        'PASSWORD': 'secret',
        'HOST': 'localhost',
        'PORT': '',
        'ATOMIC_REQUEST': True,
    }
}

我原以为,当请求以异常结束时,所有数据库事务都会回滚,但我可以看到模型InventoryActionKardex中有几条记录,即使只有一个请求以return Response()结束,并且所有其他的都以例外结束。

我对原子交易有什么误解?

我发现当使用ATOMIC_REQUEST ( https://github.com/encode/django-rest-framework/issues/2034 ) 设置为数据库时,原子事务不适用于Django Rest Framework

但我也发现当您将其显式设置为视图时它可以工作:

from django.db import transaction

class InventoryExecuteActionViewSet(viewsets.ViewSet):
    permission_classes = (permissions.IsAuthenticated,)

    @transaction.atomic
    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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM