重写Django模型save方法实现购物车逻辑时触发无限循环问题
Django CartItem模型save方法无限循环问题解决方案
问题根因
你重写了CartItem的save方法后,方法内部无论是匹配到已有条目时调用cart_item.save(),还是未匹配到时调用super().save(),都会再次触发你重写的save逻辑,形成递归调用,最终陷入无限循环。
修复方案
这里提供两种可行的实现方式,你可以根据业务场景选择:
方案1:调整save方法逻辑,避免递归调用
仅对新增的实例做合并逻辑处理,已有实例更新直接走父类原生保存逻辑,同时更新已有条目时调用父类save避免二次触发重写逻辑:
import uuid from django.db import models from pprint import pprint class Cart(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self) -> str: return str(self.id) class CartItem(models.Model): cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.CASCADE) quantity = models.PositiveIntegerField() def __str__(self) -> str: return self.product.name def save(self, *args, **kwargs): # _state.adding为True代表当前是未保存的新实例,仅此时走合并逻辑 if self._state.adding: try: existing_item = CartItem.objects.get(cart=self.cart, product=self.product) existing_item.quantity += self.quantity # 直接调用父类save方法保存已有条目,不触发重写的save逻辑 super(CartItem, existing_item).save(*args, **kwargs) pprint(existing_item) # 已有条目已更新,直接终止当前新实例的保存流程 return except CartItem.DoesNotExist: pprint("-------------------------here") # 新增无匹配条目、或更新已有条目时走原生保存逻辑 super().save(*args, **kwargs)
方案2:抽离合并逻辑到自定义管理器(更推荐)
将购物车条目添加逻辑抽离到模型管理器,避免重写save方法带来的隐式副作用,逻辑更清晰也更易维护:
class CartItemManager(models.Manager): def add_to_cart(self, cart, product, quantity): item, is_created = self.get_or_create( cart=cart, product=product, defaults={"quantity": quantity} ) if not is_created: item.quantity += quantity item.save() return item class CartItem(models.Model): cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.CASCADE) quantity = models.PositiveIntegerField() # 绑定自定义管理器 objects = CartItemManager() def __str__(self) -> str: return self.product.name
后续需要添加商品到购物车时,直接调用CartItem.objects.add_to_cart(你的cart实例, 你的product实例, 购买数量)即可。
内容的提问来源于stack exchange,提问作者antoninislam
相关产品推荐
相关产品推荐

