reactjsdjango-modelsdjango-rest-frameworkdjango-serializeractive-model-serializers

How to add different many-to-many field into a single instance or row of a foreign key in django-rest-framework using model serializer


I am creating an ecommerce website using Django-rest-framework and react. I am trying to add items to cart. I am able to add items to the cart on the frontend but I want to store the cart data to the backend (Django) database so that whenever the user add items to cart and reloads the page the items should still be in his cart like any other ecommerce site. Here is my Code for django models, serializers, viewset.

class Products(models.Model):
    title = models.CharField(max_length=100)
    image = models.URLField()
    description = models.TextField(max_length=500, blank=True, null=True)
    category = models.CharField(max_length=50, blank=True, null=True)
    rating = models.IntegerField(blank=True, null=True)
    price = models.FloatField()

    def __str__(self):
        return f"{self.id}"

class Cart(models.Model):
    product = models.ManyToManyField(
        Products, related_name="cart")
    buyer = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="cart")

    def __str__(self) -> str:
        return f"{self.buyer}"
class ProductsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Products
        fields = '__all__'

class CartSerializer(serializers.ModelSerializer):
    class Meta:
        model = Cart
        fields = '__all__'

class ProductsViewSet(viewsets.ModelViewSet):
    queryset = Products.objects.all()
    serializer_class = ProductsSerializer

class CartViewSet(viewsets.ModelViewSet):
    queryset = Cart.objects.all()
    authentication_classes = [JWTAuthentication]
    permission_classes = [
        permissions.IsAuthenticated
    ]
    serializer_class = CartSerializer

router = routers.DefaultRouter()
router.register('products', ProductsViewSet, 'products')
router.register('cart', CartViewSet, 'cart')

I am using postman to post the cart items. I am able to add more than one products for a single buyer.

but the problem is when i again add another product to the same user using postman i added before, it creates another row for the same user.

I do not want that. I want a single instance or row of a user in cart table and add as many products as i want. when i post other products for the same user, that product should also get added up in the single user row or instance. What is the best way to achieve my goal.


Solution

  • Here is the issue, Django can't automatically do that because it doesn't know which behaviour is expected. If you look at the code to add a product and look at the code to add a cart, it's exactly the same. So behaviour will also be the same.

    For the behaviour that you want, you will have to override the create method of your ModelViewSet. Here are the steps to achieve what you want -

    1. Check whether or not the user with that id already has a cart.

    2. If they have a cart, then you'll need to fetch the cart object belonging to that user and add products to it.

    3. If they don't, then you'll have to create a new cart object and do the default thing.

      class CartViewSet(viewsets.ModelViewSet):
          queryset = Cart.objects.all()
          authentication_classes = [JWTAuthentication]
          permission_classes = [
              permissions.IsAuthenticated
          ]
          serializer_class = CartSerializer
      
          def create(self, request):
              # Checking whether the Cart object already exists for the buyer
              cart = Cart.objects.filter(buyer_id = request.data.get("buyer"))
      
              if len(cart)=1:
                  #If cart exists, we loop through the list of product ids and add them
                  for product_id in request.data.get("product"):
                      cart[0].product.add(get_object_or_404(Product, id = product_id ))
      
              if len(cart)=0:
                  # Doing what normally happens.
                  return super().create(request)
      
              if len(cart)>1:
                  #Error in database. One person having multiple carts. Custom error message.
      

    To check out how to add data to many-to-many fields, check this out.