Reactoo
Reactoo

Reputation: 1042

AttributeError at /api/updateorder/71 'RelatedManager' object has no attribute 'save'

I am trying to make update API for order objects. OrderItem model is in many to one relationship (with Foreignkey) with Order Model. Also, Billing Details model is one to one relationship with Order model. I am able to update Billing details fields and also Order fields, but can't update the OrderItem fields.

I got this error saying:

'RelatedManager' object has no attribute 'save'

My models:

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)
    #billing_details = models.OneToOneField('BillingDetails',on_delete=models.CASCADE,null=True,blank=True,related_name="order")

    def __str__(self):
        return self.user.email

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

class OrderItem(models.Model):
    #user = models.ForeignKey(User,on_delete=models.CASCADE, blank=True)
    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)
    # def price(self):
    #     total_item_price = self.quantity * self.item.varaints.price
    #     return total_item_price
    total_item_price = models.PositiveIntegerField(blank=True,null=True,)
    

    def __str__(self):
        return f"{self.quantity} items of {self.item} of {self.order.user}"

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

class BillingDetails(models.Model):
    PAYMENT_TYPE = (
        ('cash_on_delivery', 'Cash On Delivery',),
        ('credit/debit_card', 'Credit/Debit Card',),
        ('connect_ips', 'Connect IPS',),
        ('fonepay', 'Fonepay',),
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    order = models.OneToOneField(Order, on_delete=models.CASCADE, blank=True, null=True, related_name='billing_details')  
    address = models.CharField(max_length=100,blank=True,null=True)
    postal = models.CharField(max_length=50,blank=True,null=True)
    payment_type = models.CharField(max_length=50,blank=True,null=True,choices=PAYMENT_TYPE,default='cash_on-delivery')
    def __str__(self):
        return self.address

    class Meta:
        verbose_name_plural= "Shipping Address"

MY VIEWS:

class UpdateOrderView(UpdateAPIView):
    permission_classes = [AllowAny]
    queryset = Order.objects.all()
    serializer_class = OrderUpdateSerializer

My serializers:

class OrderUpdateSerializer(serializers.ModelSerializer):
    order_items = OrderItemSerializer(many=True)
    billing_details = BillingDetailsSerializer()

    class Meta:
        model = Order
        fields = ['ordered','order_status','order_items','billing_details']


    def update(self, instance, validated_data):
        instance.order_status = validated_data.get('order_status')
        instance.ordered = validated_data.get('ordered')

        billing_details_data = validated_data.pop('billing_details',None)
        if billing_details_data is not None:
            instance.billing_details.address = billing_details_data['address']
            instance.billing_details.save() #no-error here


        order_items_data = validated_data.pop('order_items')
        for order_items_data in order_items_data:
            instance.order_items.quantity = order_items_data['quantity']
            instance.order_items.order_item_status = order_items_data['order_item_status']
            instance.order_items.save() #got the error here
            #super(OrderUpdateSerializer, self).update(instance.order_items,validated_data).save()
        #super().save()
        

        return super().update(instance,validated_data)

There is no error in instance.billing_details.save(), but the same code instance.order_items.save() generates the above error. If I remove instance.order_items.save(), then it works but it doesn't save the updated data in the database.

Upvotes: 2

Views: 310

Answers (1)

Ralf
Ralf

Reputation: 16515

You are using OneToOneField in one but ForeignKey in the other model, so you cannot use the same code for both.

class OrderItem(models.Model):
    ...
    order = models.ForeignKey(..., related_name='order_items')
    ...
 
class BillingDetails(models.Model):
    ...
    order = models.OneToOneField(..., related_name='billing_details')  
    ...

The instance.billing_details will return an BillingDetails instance (if there is one) because it is a OneToOneField, but instance.order_items will return a related manager for OrderItem, not an instance.

Solutions:

  1. change the field OrderItem.order to OneToOneField; but I don't know if that fits your use case, so you have to analyze it in detail if it will work for you.

  2. instance.order_items.first() (or instance.order_items.get(...)) to get get an instance from the related manager.

Upvotes: 2

Related Questions