Django ORM多对多关系中无法保存多个对象副本的问题
解决Django多对多关系无法添加同一对象多个副本的问题
你现在的代码里,每次循环调用Tickets.objects.get(id=int(ticket_id))拿到的都是数据库里同一个Ticket实例——哪怕你修改了它的count并保存,它的主键ID还是原来的那个。Django的多对多关系本身是用来关联不同对象实例的,重复添加同一个实例只会保留一条关联记录,这就是最终只存了一个的核心原因。
下面分两种业务场景给出解决方案:
方案一:用中间表记录票的数量(推荐,适合「票种」场景)
如果Tickets模型代表的是票的种类(比如「成人票」「儿童票」),不需要为每张票创建独立实例,只需要记录每种票的购买数量,那应该给多对多关系添加中间表来存储数量字段:
- 修改模型结构:
from django.db import models class Basket(models.Model): id = models.TextField(verbose_name='ID Корзины', primary_key=True) tickets = models.ManyToManyField( 'Tickets', verbose_name='Билеты', through='BasketTicket' # 指定关联的中间表 ) date = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания') def to_json(self): return { 'id': self.id, 'tickets': [ { 'ticket': ticket.to_json(), 'quantity': bt.quantity } for ticket, bt in self.tickets.through.objects.filter(basket=self).values_list('ticket', 'quantity') ], 'date': self.date } class Meta: verbose_name = 'Корзина' verbose_name_plural = 'Корзины' # 多对多关系的中间表 class BasketTicket(models.Model): basket = models.ForeignKey(Basket, on_delete=models.CASCADE) ticket = models.ForeignKey('Tickets', on_delete=models.CASCADE) quantity = models.IntegerField(default=1, verbose_name='数量') class Meta: unique_together = ('basket', 'ticket') # 确保同一购物车同一种票只存一条记录
- 更新视图逻辑:
def add_to_basket(request): if request.method == 'POST': basket_id = request.session.get('BID') if not basket_id: basket_id = str(uuid.uuid4()) request.session['BID'] = basket_id basket, created = Basket.objects.get_or_create(id=basket_id) ticket_id = request.POST.get('ticket_id') count = int(request.POST.get('count', 1)) ticket = Tickets.objects.get(id=int(ticket_id)) if ticket.count >= count: # 更新原票的库存 ticket.count -= count ticket.save() # 更新购物车:已有该票则增加数量,没有则创建新记录 basket_ticket, bt_created = BasketTicket.objects.get_or_create( basket=basket, ticket=ticket, defaults={'quantity': count} ) if not bt_created: basket_ticket.quantity += count basket_ticket.save() return JsonResponse({'status': 'ok'}) else: return JsonResponse({'status': 'not enough tickets'}) else: return JsonResponse({'status': 'not get method'})
方案二:创建独立的Ticket实例(适合「单张票」场景)
如果Tickets模型代表的是单张实体票(每张票有唯一编号、座位号等独立属性),那需要为每个要添加的票创建新实例:
修改视图中的循环逻辑:
if ticket.count >= int(count): for i in range(int(count)): # 复制原票的属性,创建新的Ticket实例(主键自动生成) new_ticket = Tickets.objects.create( # 这里列出Tickets模型的所有字段,根据你的实际结构调整 name=ticket.name, price=ticket.price, seat_number=f"{ticket.seat_prefix}-{i+1}", # 示例:生成唯一座位号 count=1 # 单张票的库存设为1 # 其他字段... ) basket.tickets.add(new_ticket) # 减少原票种的库存(如果原ticket是库存记录的话) ticket.count -= int(count) ticket.save() basket.save() return JsonResponse({'status': 'ok'})
这种方式会在数据库中创建多个独立的Ticket记录,适合每张票有专属属性的场景。
内容的提问来源于stack exchange,提问作者fdf228
相关产品推荐
相关产品推荐

