0

I am trying to implement Coupon code feature in Django Rest framework. When a user tries to purchase a product and wants to use coupon codes presented on the item, the user should be able to get a discount based on that coupon code. The coupon should be vaild ie should be within the valid till date and also should be active. But when I try to make an order object from postman without sending the coupon codes, I got the following error.

AttributeError: 'QuerySet' object has no attribute 'active'

Before the application of Coupon code feature, I was able to make an order.

My models:

class Coupons(models.Model):
    product = models.ForeignKey(Product,on_delete=models.CASCADE,
                                blank=True,null=True,related_name="coupons")
    code = models.CharField(max_length=50,unique=True)
    valid_form = models.DateField()
    valid_till = models.DateField()
    active = models.BooleanField(default=False)
    discount = models.IntegerField(default=0)

    def __str__(self):
        return self.code

Order model:

Here every calculation for the prices are done.

class Order(models.Model):
   
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    order_ID = models.CharField(max_length=12, editable=False, default=id_generator)
    #order_items = models.ManyToManyField('OrderItem',blank=True, null=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)
    current_points = models.FloatField(default=0)
    total_price = models.FloatField(blank=True,null=True)
    point_spent = models.FloatField(default=0)
    coupon_code = models.CharField(max_length=15, blank=True, null=True)


    def final_price(self):
        total = sum([_.price_1 for _ in self.order_items.all()])
        total -= Decimal(self.price_of_points)
        dis = self.price_of_coupon
        print(dis)
        total = total - dis
        return total

    @property
    def price_of_coupon(self):
        coupon_code = self.coupon_code
        items = OrderItem.objects.filter(order=self.id)
        coupon_query = Coupons.objects.filter(code=coupon_code)
        if coupon_query.active is True:
            if coupon_query.valid_form <= _datetime.date.today() and coupon_query.valid_till <= _datetime.date.today():
                coupon_discount = coupon_query.discount
                for item in items:
                    if item.order == coupon_query.id:
                        return coupon_discount
            else:
                return coupon_query.discount
        else:
            return coupon_query.discount


    @property
    def price_of_points(self):
        point_spent = self.point_spent

        if point_spent == 0:
            return 0.0
        elif point_spent <= 10000.0 and point_spent >0:
            return 10.0
        else:
            return 75.0

    def save(self, *args, **kwargs):
        self.total_price = self.final_price()
        super(Order, self).save(*args, **kwargs)

Here the error is shown in the line if coupon_query.active is True: But I have active field in the coupon model. I don't know what is the issue.

My serializer:

class OrderSerializer(serializers.ModelSerializer):
    billing_details = BillingDetailsSerializer()
    order_items = OrderItemSerializer(many=True)
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
    #total_price = serializers.SerializerMethodField(source='get_total_price')
    coupon_code = serializers.CharField(required=False)
    class Meta:
        model = Order
        fields = ['id','user','ordered_date','order_status','order_ID', 'ordered', 'order_items',
                  'total_price','point_spent','coupon_code', '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)
            ordered = validated_data.get('ordered')
            order.ordered = True
            order.save()
            return order
        else:
            raise serializers.ValidationError("This is not a customer account.Please login as customer.")

2 Answers 2

1

You have to use get instead of filter. coz, filter return list of records and get return one only record.

Also remember, if there is multiple records with same code, then get will fire you error. so make sure your database not storing duplicate code.

Try like this:

coupon_query = Coupons.objects.get(code=coupon_code)
  

Also the better approach is using get_object_or_404 methord.

like this

coupon_query = get_object_or_404(Coupons, code=coupon_code) 

If you don't use get_object_or_404 method, then it will fire you server error if no records founds with the code.

If the user don't want to add coupon code, then you have to first handle it some other condition

Also i notice you are comparing the date later again which is not a best approach.

So the best it, you try like this

coupon_query = Coupons.objects.filter(code=coupon_code, valid_from__get=datetime.today(), valid_till__lt=datetime.today())

if couon_query.exists():
    validated_couopon = coupon_query.first()
else:
    raise ValidationError("Your Couon not valid")
Sign up to request clarification or add additional context in comments.

5 Comments

The thing is that, user might not use coupon codes everytime. So it will generate 404 error everytime user place an order without using coupon code.
For this, you can handle the exception based on it. I Just tried solve the actual problem, not other cases
@Django-Rocks You got the case? If user don't submit coupon code, then dont execuate the block of filtring, just put it in another condion
@Django-Rocks I have updated answer more deeply
It's my pleasure that i could solve your problem
0

Model.objects.filter() returns a Queryset, which is a list of models not a single model instance. But Model.objects.get() gives you a single model instance

so change

coupon_query = Coupons.objects.filter(code=coupon_code)

to

try:
     coupon_query = Coupons.objects.get(code=coupon_code)
except:
     coupon_query = None

if coupon_query is not None and coupon_query.active is True:
   ...

that should work

2 Comments

I tried with get but I got the error saying Coupons matching query does not exist.
I need to place an order even when there is no coupon code sent from the frontend. My code only works when there is coupon code sent in the request payload.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.