You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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">&times;</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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:45:53