Search code examples
djangoapidjango-rest-frameworkpostmanunique

(1062, "Duplicate entry '81CLZECZRW' for key 'orders_orderitem.orders_orderitem_orderItem_ID_7d6cd69b_uniq'") on Django Rest Framework


I am trying to generate a uniq OrderItem_ID during the order create api. But, it generates the above error as django.db.utils.IntegrityError:

The first api is always successful from the postman, but in the second call I tried changing different products for creating the order, but I am getting this unique id order.

I have to remove the order_items from db , to create a new order_item object otherwise I get this unique error.

I am sending data like this.

enter image description here

My model:

import random
import string
# Create your models here.

def id_generator(size=10, chars=string.ascii_uppercase + string.digits):
   return ''.join(random.choice(chars) for _ in range(size))

class Order(models.Model):
    ORDER_STATUS = (
        ('To_Ship', 'To Ship',),
        ('Shipped', 'Shipped',),
        ('Delivered', 'Delivered',),
        ('Cancelled', 'Cancelled',),
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)    
    order_status = models.CharField(max_length=50,choices=ORDER_STATUS,default='To_Ship')

    ordered_date = models.DateTimeField(auto_now_add=True)
    ordered = models.BooleanField(default=False)
    total_price = models.CharField(max_length=50,blank=True,null=True)
    
    def __str__(self):
        return self.user.email

    class Meta:
        verbose_name_plural = "Orders"
        ordering = ('-id',)

class OrderItem(models.Model):    
    orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator())
    order = models.ForeignKey(Order,on_delete=models.CASCADE, blank=True,null=True,related_name='order_items')
    item = models.ForeignKey(Product, on_delete=models.CASCADE,blank=True, null=True)
    order_variants = models.ForeignKey(Variants,on_delete=models.CASCADE,blank=True,null=True)
    quantity = models.IntegerField(default=1)    
    total_item_price = models.PositiveIntegerField(blank=True,null=True,)

My serializers:

class OrderSerializer(serializers.ModelSerializer):

    billing_details = BillingDetailsSerializer()
    order_items = OrderItemSerializer(many=True)
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
    class Meta:
        model = Order
        fields = ['id','user','ordered_date','order_status', 'ordered', 'order_items', 'total_price','billing_details']
        # depth = 1   

    def create(self, validated_data):
        user = self.context['request'].user
        if not user.is_seller:
            order_items = validated_data.pop('order_items')
            billing_details = validated_data.pop('billing_details')
            order = Order.objects.create(user=user,**validated_data)
            BillingDetails.objects.create(user=user,order=order,**billing_details)
            for order_items in order_items:
                OrderItem.objects.create(order=order,**order_items)
           
            return order
        else:
            raise serializers.ValidationError("This is not a customer account.Please login as customer.")

In python shell, i tired this and it works fine

enter image description here


Solution

  • the problem is with

    orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator())
    

    Here in the default, you're have assigned function call. Thus, it will only be evaluated once at the time of creation, .i.e., at first time you run makemigrations.

    We need to have function references in the default values, this way it will be called each time a new instance is created.

    Try replacing the line with

    orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator)
    

    Note default=id_generator and not default=id_generator().

    Hope this answers your question. PS: you would be required to rerun the makemigrations and migrations commands to set this change into effect.