Search code examples
pythondjangodjango-rest-frameworkdjango-viewsoverriding

Django: how to change status of cart item, if its quantity more than product's general_quantity?


My models.py:

class CartModel(models.Model):

    CART_STATUS = (
        ('n', 'Новая'),
        ('o', 'Заказана'),
        ('d', 'Удалена'),
    )

    status = models.CharField(
        max_length=1, choices=CART_STATUS, blank=True, default='n')
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(
        UserModel, on_delete=models.CASCADE, related_name='cart', verbose_name='Принадлежит')
    created_at = models.DateTimeField(
        auto_now_add=True, verbose_name="Дата создания")

    class Meta:
        verbose_name = "Корзина"
        verbose_name_plural = "Корзина"
        ordering = ('-created_at',)


class CartItemModel(models.Model):

    CARTITEM_STATUS = (
        ('a', 'Доступен'),
        ('n', 'Недоступен'),
        ('d', 'Удалён'),
    )

    status = models.CharField(
        max_length=1, choices=CARTITEM_STATUS, blank=True, default='a')
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    product = models.ForeignKey(
        ProductModel, on_delete=models.CASCADE, related_name='items', verbose_name="Продукт")
    quantity = models.IntegerField(verbose_name="Количество")
    cart = models.ForeignKey(
        CartModel, on_delete=models.CASCADE, related_name='items', verbose_name="Корзина")

    class Meta:
        verbose_name = "Товар корзины"
        verbose_name_plural = "Товары корзины"


class ProductModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=82, verbose_name='Товар')
    category = models.ForeignKey(
        ProductCategoryModel, on_delete=models.CASCADE, verbose_name='Категория', related_name='products')
    description = models.TextField(max_length=256, verbose_name='Описание')
    price = models.DecimalField(
        max_digits=6, decimal_places=2, verbose_name='Цена')
    general_quantity = models.IntegerField(verbose_name='Общее количество')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "Товар"
        verbose_name_plural = "Товары"

My serializers.py

class CartItemSerializer(serializers.ModelSerializer):
    singe_item_price = serializers.SerializerMethodField('get_item_price')

    def get_item_price(self, obj):
        return obj.product.price

    class Meta:
        model = CartItemModel
        exclude = ('cart',)


class CartSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
    items = CartItemSerializer(many=True, required=True)
    total_sum = serializers.SerializerMethodField('get_field_name')

    def get_field_name(self, obj):
        return sum([i.quantity*i.product.price for i in obj.items.all()])

    class Meta:
        model = CartModel
        # exclude = ('user',)
        fields = ('id', 'status', 'created_at', 'total_sum', 'items')

My views.py:

class CartAPIView(viewsets.ModelViewSet):
    def perform_create(self, serializer):
        serializer.validated_data['user'] = self.request.user
        serializer.save()

    queryset = CartModel.objects.all()
    serializer_class = CartSerializer


class CartItemAPIView(viewsets.ModelViewSet):

    queryset = CartModel.objects.all()
    serializer_class = CartItemSerializer

So, if in Cart post-method quantity of item is more than general_quantity of product, status field have to change from default 'a' (availble) to 'n' (not available)

I tried override my CartAPIView perform_create function like this, but nothing changed:

class CartAPIView(viewsets.ModelViewSet):
    def perform_create(self, serializer):
        serializer.validated_data['user'] = self.request.user
        for i in serializer.validated_data['items']:
            if i['quantity'] > i['product'].general_quantity:
                i['status'] = 'n'
        serializer.save()

Swagger get cart: { "id": "7c399665-99f3-40cf-b638-89bdbe9d52a1", "status": "n", "created_at": "2023-03-14T20:02:53.763334+06:00", "total_sum": 4400, "items": [ { "id": "525543d8-dc3a-4e0c-adaf-153ed9ec4c06", "singe_item_price": 400, "status": "a", "quantity": 11, "product": "af00fa7f-ff76-4657-a33c-3e5cde1e8830" } ] }

General quantity is 10, but status still 'a'. Maybe it's better to make that logic in serializer, than in a view


Solution

  • You can modify your CartItemSerializer to check the quantity and update the status in the validate() method, like so:

    class CartItemSerializer(serializers.ModelSerializer):
        singe_item_price = serializers.SerializerMethodField('get_item_price')
    
        def get_item_price(self, obj):
            return obj.product.price
    
        def validate(self, data):
            """
            Check that quantity is not greater than general_quantity
            """
            if data['quantity'] > data['product'].general_quantity:
                data['status'] = 'n'
            return data
    
        class Meta:
            model = CartItemModel
            exclude = ('cart',)
    

    Note: Models in don't require Model to be added as suffix, so it is better to name the models as CartItem, Cart and Product from CartItemModel, CartModel and ProductModel respectively.