Search code examples
djangodjango-rest-frameworkdjango-serializer

additional fields in ModelSerializer with override update in django rest framework


I have a product model like this:

 class Product(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField(blank=True, null=True)
    category = models.ForeignKey(ProductCategories, on_delete=models.CASCADE)
    brand = models.ForeignKey(ProductBrand, on_delete=models.SET_NULL, blank=True, null=True)
    score = models.PositiveIntegerField(blank=True, null=True)
    insurnace = models.PositiveIntegerField(blank=True, null=True)
    property = models.ManyToManyField(ProductProperty, blank=True)
    is_flagship = models.BooleanField(default=False)
    is_exhibition = models.BooleanField(default=False)
    seen = models.IntegerField(default=0)

for saving images, prices and details i wrote this three model:

class ProductImage(models.Model):
    image = models.ImageField(upload_to='product_image/')
    product = models.ForeignKey(Product, related_name='image', on_delete=models.SET_NULL, blank=True, null=True)
    is_main_image = models.BooleanField(default=False)

class ProductDetail(models.Model):
    title = models.ForeignKey(ProductDetailTitle, related_name='value', on_delete=models.CASCADE)
    value = models.CharField(max_length=500)
    product = models.ForeignKey(Product, related_name='detail', on_delete=models.CASCADE)

class ProductPrice(models.Model):
    product = models.ForeignKey(Product, related_name='price', on_delete=models.CASCADE)
    color = models.ForeignKey(ProductColor, on_delete=models.CASCADE)
    price = models.PositiveIntegerField()
    quantity = models.PositiveIntegerField()

i wrote this serializer for manage product

class ProductAdminSerializer(serializers.ModelSerializer):
    images = serializers.ListSerializer(child=serializers.IntegerField())
    prices = ProductPriceForProductAdminSerializer(many=True)
    properties = serializers.ListSerializer(child=serializers.IntegerField())
    details = ProductDetailForProductAdminSerializer(many=True)

    class Meta:
        model = Product
        fields = ['id', 'title', 'category', 'brand', 'images', 'description', 'properties', 'is_flagship',
                  'is_exhibition', 'prices', 'score', 'insurnace', 'details']

    def create(self, validated_data):
        title = validated_data.get('title')
        category = validated_data.get('category')
        brand = validated_data.get('brand')
        description = validated_data.get('description')
        is_flagship = validated_data.get('is_flagship')
        is_exhibition = validated_data.get('is_exhibition')
        score = validated_data.get('score')
        insurnace = validated_data.get('insurnace')

        product = Product.objects.create(title=title, category=category, brand=brand,
                                         description=description, is_flagship=is_flagship, is_exhibition=is_exhibition,
                                         score=score, insurnace=insurnace)

        image_ids = validated_data.get('images')
        ProductImage.objects.filter(id__in=image_ids).update(product=product)

        properties = validated_data.get('properties')
        if properties is not None:
            property_objs = ProductProperty.objects.filter(id__in=properties)
            product.property.set(property_objs)

        prices = validated_data.get('prices')
        if prices is not None:

            prices_obj = list()

            for price_obj in prices:
                color = price_obj.get('color')
                price = price_obj.get('price')
                quantity = price_obj.get('quantity')

                prices_obj.append(ProductPrice(product=product, color=color, price=price, quantity=quantity))
            ProductPrice.objects.bulk_create(prices_obj)

        details = validated_data.get('details')
        if details is not None:

            details_obj = list()

            for detail in details:
                title = detail.get('title')
                value = detail.get('value')

                details_obj.append(ProductDetail(product=product, title=title, value=value))

            ProductDetail.objects.bulk_create(details_obj)

        return validated_data

    def update(self, instance, validated_data):
        images = validated_data.pop('images', None)
        properties = validated_data.pop('properties', None)
        prices = validated_data.pop('prices', None)
        details = validated_data.pop('details', None)

        for attr, value in validated_data.items():
            setattr(instance, attr, value)

        if images is not None:
            ProductImage.objects.filter(product=instance).exclude(id__in=images).delete()
            ProductImage.objects.filter(id__in=images).update(product=instance)

        if properties is not None:
            property_objs = ProductProperty.objects.filter(id__in=properties)
            instance.property.set(property_objs)

        return instance

the create method is ok and work well. my problem is with update after send patch request, everything in database getting update as should, but at the end i get this error

AttributeError: Got AttributeError when attempting to get a value for field images on serializer ProductAdminSerializer. The serializer field might be named incorrectly and not match any attribute or key on the Product instance. Original exception text was: 'Product' object has no attribute 'images'.

how can I fix that?


Solution

  • You have a field images on the serializer, but not on the Product model. To serialize your instance, the serializer try to find a field named "images" on the model, which leads to this error.

    A solution could be to use the field "images" only for saving objects with write_only

    class ProductAdminSerializer(serializers.ModelSerializer):
        images = serializers.ListSerializer(child=serializers.IntegerField(), write_only=True)
    

    If you need the list of the images in the response, you can create another field with read_only.