Search code examples
djangodjango-rest-frameworkpostmaniterable

TypeError: 'Variants' object is not iterable in django rest framework


Here I am trying to create an add-products API. The product model is related to Category,Brand, Collection, Picture, and Variants models.

Here Category, Picture, and Variants are M2M relationships with the Product model. From the frontend, all the chunks of data are sent at once to create a product object. I have added categories and pictures using .set() method.

But for variants, I have to create variants objects first and then add them to product. How to do this. I tried but I got this error.

TypeError: 'Variants' object is not iterable

I am sending raw data like this.

enter image description here

My models:

class Variants(models.Model):
    SIZE = (
        ('not applicable', 'not applicable',),
        ('S', 'Small',),
        ('M', 'Medium',),
        ('L', 'Large',),
        ('XL', 'Extra Large',),
    )
    AVAILABILITY = (
        ('available', 'Available',),
        ('not_available', 'Not Available',),
    )
    product_id = models.CharField(max_length=70,default='OAXWRTZ_12C',blank=True)
    price = models.DecimalField(decimal_places=2, max_digits=20,default=500)
    size = models.CharField(max_length=50, choices=SIZE, default='not applicable',blank=True,null=True)
    color = models.CharField(max_length=70, default="not applicable",blank=True,null=True)
    variant_image = models.ImageField(upload_to="products/images", blank=True)
    thumbnail = ImageSpecField(source='variant_image',
                               processors=[ResizeToFill(100, 50)],
                               format='JPEG',
                               options={'quality': 60})
    quantity = models.IntegerField(default=10,blank=True,null=True)  # available quantity of given product
    variant_availability = models.CharField(max_length=70, choices=AVAILABILITY, default='available')

    class Meta:
        verbose_name_plural = "Variants"

    def __str__(self):
        return self.product_id

class Product(models.Model):   
    
    merchant = models.ForeignKey(Seller,on_delete=models.CASCADE,blank=True,null=True)
    category = models.ManyToManyField(Category, blank=False)
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
    collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
    featured = models.BooleanField(default=False)  # is product featured?
    variants = models.ManyToManyField(Variants,related_name='products')

My views:

class ProductAddAPIView(CreateAPIView):
    permission_classes = [AllowAny]
    queryset = Product.objects.all()
    serializer_class = AddProductSerializer

My serializers:

class  AddProductSerializer(serializers.ModelSerializer):    
    variants = VariantSerializer(many=True)
    
    class Meta:
        model = Product
        fields = ['merchant','featured', 'top_rated','category','brand','collection',
                  'name','description', 'main_product_image','best_seller','picture',
                  'rating','availability','warranty','services','variants']
        # depth = 1

    def create(self, validated_data):
         #user = self.context['request'].user
         picture_data = validated_data.get('picture')
         merchant = validated_data.get('merchant')
         category_data = validated_data.get('category')
         featured = validated_data.get('featured')
         top_rated = validated_data.get('top_rated')
         brand = validated_data.get('brand')
         collection = validated_data.get('collection')
         name = validated_data.get('name')
         description = validated_data.get('description')
         main_product_image = validated_data.get('main_product_image')
         best_seller = validated_data.get('best_seller')
         rating = validated_data.get('rating')
         availability = validated_data.get('availability')
         warranty = validated_data.get('warranty')
         services = validated_data.get('services')

        #variants_logic

         variants_data = validated_data.get('variants')
         for variants_data in variants_data:
             abc = Variants.objects.create(**variants_data)

         #products-logic

         product = Product.objects.create(featured=featured,top_rated=top_rated,
                                          brand=brand,collection=collection,
                                          name=name,description=description,
                                          main_product_image=main_product_image,
                                          best_seller=best_seller,rating=rating,
                                          availability=availability,warranty=warranty,
                                          services=services,merchant=merchant)
         product.save()
         #category_data = Category.objects.filter(category__in=category_data)
         product.category.set(category_data)
         product.picture.set(picture_data)
         product.variants.set(abc)
         return product

My URLs:

path('api/addproducts', views.ProductAddAPIView.as_view(), name='api-addproducts'),

The issue is shown on the line product.variants.set(abc)


Solution

  • I used .add method inside the loop instead of .set() and it worked. Learnt a great thing today.

    My working serializer is:

    class  AddProductSerializer(serializers.ModelSerializer):    
        variants = VariantSerializer(many=True)
       
        class Meta:
            model = Product
            fields = ['merchant','featured', 'top_rated','category','brand','collection',
                      'name','description', 'main_product_image','best_seller','picture',
                      'rating','availability','warranty','services','variants']
            # depth = 1
    
        def create(self, validated_data):
             #user = self.context['request'].user
             picture_data = validated_data.get('picture')
             merchant = validated_data.get('merchant')
             category_data = validated_data.get('category')
             featured = validated_data.get('featured')
             top_rated = validated_data.get('top_rated')
             brand = validated_data.get('brand')
             collection = validated_data.get('collection')
             name = validated_data.get('name')
             description = validated_data.get('description')
             main_product_image = validated_data.get('main_product_image')
             best_seller = validated_data.get('best_seller')
             rating = validated_data.get('rating')
             availability = validated_data.get('availability')
             warranty = validated_data.get('warranty')
             services = validated_data.get('services')
    
            #variants_logic
    
             variants_data = validated_data.get('variants')
    
    
             #products-logic
    
             product = Product.objects.create(featured=featured,top_rated=top_rated,
                                              brand=brand,collection=collection,
                                              name=name,description=description,
                                              main_product_image=main_product_image,
                                              best_seller=best_seller,rating=rating,
                                              availability=availability,warranty=warranty,
                                              services=services,merchant=merchant)
             product.save()
             #category_data = Category.objects.filter(category__in=category_data)
             product.category.set(category_data)
             product.picture.set(picture_data)
             for variants_data in variants_data:
                 abc = Variants.objects.create(**variants_data)
                 product.variants.add(abc)
             
             return product