Django项目添加商品数量增减按钮及库存同步功能需求
实现Django商品数量管理功能方案
1. 模型层修改(models.py)
新增库存字段到商品模型,并封装库存扣减逻辑,同时添加订单模型记录用户购买行为:
# models.py from collections.abc import Collection from django.contrib.auth.models import User from django.db import models from django.core.exceptions import ValidationError class Client(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) mobile = models.CharField(max_length=15) address = models.CharField(max_length=255) def clean_mobile(self): mobile = self.mobile if len(str(mobile)) != 10: raise ValidationError("无效的手机号码") return mobile class Supplier(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) product_name = models.CharField(max_length=255) product_price = models.DecimalField(max_digits=10, decimal_places=2) image = models.ImageField(upload_to='products/') # 添加库存字段,默认值为0 stock = models.PositiveIntegerField(default=0, verbose_name="库存数量") def __str__(self): return self.product_name # 封装库存扣减方法,确保库存不小于0 def deduct_stock(self, quantity): if self.stock >= quantity: self.stock -= quantity self.save() return True return False # 新增订单模型,关联客户和商品,记录购买数量 class Order(models.Model): client = models.ForeignKey(Client, on_delete=models.CASCADE) product = models.ForeignKey(Supplier, on_delete=models.CASCADE) quantity = models.PositiveIntegerField(default=1) created_at = models.DateTimeField(auto_now_add=True) def save(self, *args, **kwargs): # 保存订单前自动扣减库存,库存不足则抛出错误 if self.product.deduct_stock(self.quantity): super().save(*args, **kwargs) else: raise ValidationError("库存不足,无法完成订单")
2. 视图层更新(views.py)
修改供应商仪表盘支持库存录入,新增客户商品列表和订单提交视图:
# views.py from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib import messages from .models import Supplier, Order, Client from django.core.exceptions import ValidationError @login_required def supplier_dashboard(request): if request.method == 'POST': product_name = request.POST.get('product_name') product_price = request.POST.get('product_price') image = request.FILES.get('image') stock = request.POST.get('stock', 0) Supplier.objects.create( user=request.user, product_name=product_name, product_price=product_price, image=image, stock=int(stock) ) messages.success(request, "商品添加成功") return redirect('supplier_dashboard') suppliers = Supplier.objects.filter(user=request.user) return render(request, 'enroll/supplier_dashboard.html', {'suppliers': suppliers}) # 客户商品列表视图,展示有库存的商品 @login_required def customer_products(request): client = get_object_or_404(Client, user=request.user) products = Supplier.objects.filter(stock__gt=0) return render(request, 'enroll/customer_products.html', {'products': products, 'client': client}) # 订单提交视图,处理库存扣减逻辑 @login_required def place_order(request, product_id): if request.method == 'POST': quantity = int(request.POST.get('quantity', 1)) product = get_object_or_404(Supplier, id=product_id) client = get_object_or_404(Client, user=request.user) try: Order.objects.create(client=client, product=product, quantity=quantity) messages.success(request, f"成功购买{quantity}件{product.product_name}") except ValidationError as e: messages.error(request, str(e)) return redirect('customer_products') product = get_object_or_404(Supplier, id=product_id) return render(request, 'enroll/place_order.html', {'product': product})
3. 模板修改
3.1 供应商仪表盘模板(supplier_dashboard.html)
添加库存输入字段,并在商品列表中显示库存:
<!-- 省略头部不变部分 --> <div class="col-sm-3"> <h1 class="text-center alert alert-info">添加商品</h1> <form action="" method="POST" enctype="multipart/form-data"> {% csrf_token %} <label for="product_name">商品名称:</label> <input type="text" id="product_name" class="form-control" name="product_name" required><br> <label for="product_price">商品价格:</label> <input type="text" id="product_price" class="form-control" name="product_price" required><br> <label for="stock">库存数量:</label> <input type="number" id="stock" class="form-control" name="stock" min="0" required><br> <label for="supplier_images">图片:</label> <input type="file" name="image" required><br><br> <input type="submit" value="添加" class="btn btn-success"> </form> </div> <div class="col-sm-9"> <h1 class="text-center alert alert-info">商品列表</h1> <table class="table table-striped"> <thead> <tr> <th scope="col">供应商名称</th> <th scope="col">商品名称</th> <th scope="col">商品价格</th> <th scope="col">库存数量</th> <th scope="col">商品图片</th> <th scope="col">操作</th> </tr> </thead> <tbody> {% for supplier in suppliers %} <tr> <td scope="row">{{supplier.user}}</td> <td scope="row">{{supplier.product_name}}</td> <td scope="row">{{supplier.product_price}}</td> <td scope="row">{{supplier.stock}}</td> <td scope="row"><img src="{{supplier.image.url}}" alt="image" style="height: 50px;width:50px"></td> <td scope="row"> <div class="d-inline-block"> <a href="{% url 'supplier_edit' supplier.id %}" class="btn btn-warning">编辑</a> </div> <div class="d-inline-block"> <a href="{% url 'supplier_delete' supplier.id %}" class="btn btn-danger" onclick="return confirm('确定要删除该商品吗?')">删除</a> </div> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- 省略尾部不变部分 -->
3.2 客户商品列表模板(customer_products.html)
展示可购买商品,提供购买入口:
<!doctype html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> <title>客户商品列表</title> </head> <body> <div class="container mt-4"> <h1 class="text-center alert alert-info">欢迎,{{request.user.username}}</h1> {% if messages %} {% for message in messages %} <div class="alert alert-{{message.tags}} alert-dismissible fade show" role="alert"> {{message}} <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> {% endfor %} {% endif %} <div class="row"> {% for product in products %} <div class="col-md-4 mb-4"> <div class="card"> <img src="{{product.image.url}}" class="card-img-top" alt="{{product.product_name}}" style="height: 200px; object-fit: cover;"> <div class="card-body"> <h5 class="card-title">{{product.product_name}}</h5> <p class="card-text">价格: {{product.product_price}}元</p> <p class="card-text">库存: {{product.stock}}件</p> <a href="{% url 'place_order' product.id %}" class="btn btn-primary">购买</a> </div> </div> </div> {% endfor %} </div> <a href="{% url 'logout' %}" class="btn btn-success mt-3">退出登录</a> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script> </body> </html>
3.3 订单提交模板(place_order.html)
实现数量增减按钮,限制购买数量不超过库存:
<!doctype html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> <title>购买商品</title> </head> <body> <div class="container mt-4"> <h1 class="text-center alert alert-info">购买 {{product.product_name}}</h1> <div class="card mb-4"> <img src="{{product.image.url}}" class="card-img-top" alt="{{product.product_name}}" style="height: 200px; object-fit: cover;"> <div class="card-body"> <h5 class="card-title">{{product.product_name}}</h5> <p class="card-text">价格: {{product.product_price}}元/件</p> <p class="card-text">剩余库存: {{product.stock}}件</p> <form action="{% url 'place_order' product.id %}" method="POST"> {% csrf_token %} <div class="form-group"> <label for="quantity">购买数量:</label> <div class="input-group"> <button type="button" class="btn btn-outline-secondary" onclick="changeQuantity(-1)">-</button> <input type="number" id="quantity" name="quantity" class="form-control text-center" value="1" min="1" max="{{product.stock}}"> <button type="button" class="btn btn-outline-secondary" onclick="changeQuantity(1)">+</button> </div> </div> <button type="submit" class="btn btn-success">确认购买</button> <a href="{% url 'customer_products' %}" class="btn btn-secondary ml-2">返回商品列表</a> </form> </div> </div> </div> <script> function changeQuantity(delta) { const input = document.getElementById('quantity'); const current = parseInt(input.value); const min = parseInt(input.min); const max = parseInt(input.max); let newVal = current + delta; if (newVal < min) newVal = min; if (newVal > max) newVal = max; input.value = newVal; } </script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script> </body> </html>
4. URL配置补充
在项目urls.py中添加新路由:
# urls.py from django.contrib import admin from django.urls import path from django.contrib.auth import views as auth_views from . import views urlpatterns = [ path('admin/', admin.site.urls), # 供应商路由 path('supplier/dashboard/', views.supplier_dashboard, name='supplier_dashboard'), path('supplier/edit/<int:id>/', views.supplier_edit, name='supplier_edit'), path('supplier/delete/<int:id>/', views.supplier_delete, name='supplier_delete'), # 客户路由 path('customer/products/', views.customer_products, name='customer_products'), path('customer/order/<int:product_id>/', views.place_order, name='place_order'), # 登录登出 path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), ]
关键说明
- 库存扣减逻辑封装在模型层,避免业务逻辑分散,同时保证数据一致性
- 前端数量控件通过JavaScript限制范围,防止无效输入
- 订单保存时自动触发库存扣减,库存不足时抛出明确错误提示
- 若需处理高并发场景,可在库存扣减时使用
select_for_update加锁,避免超卖问题
内容的提问来源于stack exchange,提问作者Shivang Kant
相关产品推荐
相关产品推荐

