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

CS50 Web项目2:如何保存起拍价并与后续出价对比

Django拍卖网站起拍价与出价对比功能修复

问题背景

我正在开发CS50 Web编程课程的Project 2(基于Django的类eBay电商拍卖网站),需要保存拍卖的起拍价,用于和后续出价进行对比,但当前代码存在逻辑错误,无法正确实现该功能,需要技术支持。

当前代码

models.py

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    pass

class Category(models.Model):
    categoryName = models.CharField(max_length=20)

    def __str__(self):
        return self.categoryName
    
class Listing(models.Model):
    title = models.CharField(max_length=30)             
    description = models.CharField(max_length=100)                            
    imageUrl = models.CharField(max_length=500)         
    isActive = models.BooleanField(default=True)        
    price = models.IntegerField(default=0)              
    startingBid = models.IntegerField(default=0)        
    owner = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="user") 
    category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True, related_name="category") 
    watchlist = models.ManyToManyField(User, blank=True, null=True, related_name="listingWatchlist")

class Bid(models.Model):
    bid = models.IntegerField(default=0) 
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="userBid")    
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, blank=True, null=True, related_name="bidListing")           

class Comments(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="userComment")
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, blank=True, null=True, related_name="listingComment")
    comment = models.CharField(max_length=200)       

    def __str__(self):
        return f"{self.author} comment on {self.listing}"

views.py 出价添加函数

def addNewBid(request, id):

    currentUser = request.user
    listingData = Listing.objects.get(pk=id)

    isListWatchList = request.user in listingData.watchlist.all()

    allComments = Comments.objects.filter(listing=listingData)

    newBid = request.POST['newBid']

    isOwner = request.user.username == listingData.owner.username

    # 出价规则:必须至少等于起拍价,且大于已有的所有出价(如果有的话)
    # 不满足条件则返回错误提示
    if int(newBid) >= listingData.price.bid:
        updateBid = Bid(user = currentUser, bid = int(newBid))
        updateBid.save()
        listingData.price = updateBid
        listingData.save()

        return render(request, "auctions/listing.html", {
            "listing": listingData,
            "message": "Bid succesful: Current price was updated!",
            "update": True,
            "isListingWatchList": isListWatchList,
            "allComments": allComments,
            "isOwner": isOwner,
        })
    
    else:
        return render(request, "auctions/listing.html", {
            "listing": listingData,
            "message": "Bid failed!",
            "updated": False,
            "isListingWatchList": isListWatchList,
            "allComments": allComments,
            "isOwner": isOwner,
        })

listing.html 模板

{% extends "auctions/layout.html" %}

{% block body %}

     {% if message %}
        {% if updated %}
             <div class="alert alert-success" role="alert">
                {{ message }}
            </div>
        {% else %}
             <div class="alert alert-warning" role="alert">
                {{ message }}
            </div>
        {% endif %}
    {% endif %}

    {% if not listing.isActive and user == listing.price.user %}
        <div class="alert alert-success" role="alert">
            Wow! You won the auction !
        </div>
    {% endif %}

    <h2>{{ listing.title }}</h2>
    <br>
    <p>{{ listing.description }}</p>
    <h5>Current price: ${{ listing.price.bid }}</h5>
    <p>Owner: {{ listing.owner }}</p>
    
    {% if user.is_authenticated %}
        <form action="{% url 'addNewBid' id=listing.id %}" method="POST">
            {% csrf_token %}
            <div class="form-group">
                <label for="comment">New Bid:</label>
                <input type="number" min="0" name="newBid" placeholder="">
                <button type="submit" class="btn btn-primary">Add</button>
            </div>            
        </form>

        {% if isOwner and listing.isActive %}
            <form action="{% url 'closeAuction' id=listing.id %}" method="POST">
                {% csrf_token %}
                <div class="col-md-12 text-center">
                    <button type="submit" class="btn btn-primary">Close Auction</button>
                </div>
            </form>
        {% endif %}
    {% endif %}

    <img src="{{ listing.imageUrl }}" alt="{{ listing.title }}" height="400px">
    <h2>Comments</h2>    
    <ul class="list-group">
        {% for i in allComments %}
            <li class="list-group-item">{{ i.comment }}
                <br>
                <p>Author: {{ i.author }}</p>
            </li>
        {% endfor %}
    </ul>
    <br>

    {% if user.is_authenticated %}
        <form action="{% url 'addNewComment' id=listing.id %}" method="POST">
            {% csrf_token %}
            <div class="form-group">
                <label for="comment">New Comment:</label>
                <input type="text" name="newComment" placeholder="">
                <button type="submit" class="btn btn-primary">Add</button>
            </div>            
        </form>
    {% endif %}

    {% if user.is_authenticated %}
        {% if isListingWatchList %}        
            <form action="{% url 'removeWatchList' id=listing.id %}" method="POST">
                {% csrf_token %}
                <div class="col-md-12 text-center">
                    <button type="submit" class="btn btn-primary">Remove from Watchlist</button>
                </div>             
            </form>
        {% else %}                
            <form action="{% url 'addWatchList' id=listing.id %}" method="POST">
                {% csrf_token %}
                <div class="col-md-12 text-center">
                    <button type="submit" class="btn btn-primary">Add to Watchlist</button>
                </div>               
            </form>
        {% endif %}
    {% endif %}

{% endblock %}

问题分析

  1. 模型类型不匹配:Listing中的price字段定义为IntegerField,但在views中却将其赋值为Bid对象,会导致类型错误,模板访问listing.price.bid时会抛出属性不存在的异常(整数没有bid属性)。
  2. 出价逻辑缺失起拍价对比:当前代码仅对比新出价与当前price.bid,未检查是否大于等于startingBid,不符合需求。
  3. Bid记录未关联Listing:创建新Bid时未绑定对应拍卖,导致Bid数据孤立,无法追踪归属。
  4. 模板变量不一致:views中成功返回的变量是update,但模板判断的是updated,导致成功提示无法正确显示。

修复方案

1. 修正Listing模型

将price改为ForeignKey关联Bid,创建拍卖时自动生成初始Bid(值为起拍价):

class Listing(models.Model):
    title = models.CharField(max_length=30)             
    description = models.CharField(max_length=100)                            
    imageUrl = models.CharField(max_length=500)         
    isActive = models.BooleanField(default=True)        
    # 修改为ForeignKey关联Bid,允许为空(创建时自动初始化)
    price = models.ForeignKey(Bid, on_delete=models.SET_NULL, blank=True, null=True, related_name="current_listing")
    startingBid = models.IntegerField(default=0)        
    owner = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="user_listings") 
    category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True, related_name="category_listings") 
    watchlist = models.ManyToManyField(User, blank=True, null=True, related_name="listingWatchlist")

    # 重写save方法,首次创建时自动生成初始Bid
    def save(self, *args, **kwargs):
        if not self.pk:
            super().save(*args, **kwargs)
            # 创建初始Bid并关联当前拍卖
            initial_bid = Bid(bid=self.startingBid, user=self.owner, listing=self)
            initial_bid.save()
            self.price = initial_bid
            super().save(*args, **kwargs)
        else:
            super().save(*args, **kwargs)

2. 修复addNewBid函数逻辑

修正出价判断条件,关联Bid到拍卖,统一模板变量名:

def addNewBid(request, id):
    currentUser = request.user
    listingData = Listing.objects.get(pk=id)
    isListWatchList = request.user in listingData.watchlist.all()
    allComments = Comments.objects.filter(listing=listingData)
    newBid = int(request.POST['newBid'])
    isOwner = request.user == listingData.owner

    # 获取当前最高出价,无记录则用起拍价
    current_max_bid = listingData.price.bid if listingData.price else listingData.startingBid

    # 满足两个条件:大于等于起拍价,且大于当前最高出价
    if newBid >= listingData.startingBid and newBid > current_max_bid:
        updateBid = Bid(user=currentUser, bid=newBid, listing=listingData)
        updateBid.save()
        listingData.price = updateBid
        listingData.save()

        return render(request, "auctions/listing.html", {
            "listing": listingData,
            "message": "出价成功!当前价格已更新!",
            "updated": True,  # 和模板变量名统一
            "isListingWatchList": isListWatchList,
            "allComments": allComments,
            "isOwner": isOwner,
        })
    
    else:
        error_msg = "出价失败!出价不得低于起拍价!" if newBid < listingData.startingBid else "出价失败!出价必须高于当前最高价格!"
        return render(request, "auctions/listing.html", {
            "listing": listingData,
            "message": error_msg,
            "updated": False,
            "isListingWatchList": isListWatchList,
            "allComments": allComments,
            "isOwner": isOwner,
        })

3. 修复模板显示逻辑

处理价格为空的边界情况,优化出价输入限制:

<!-- 显示起拍价和当前价格 -->
<h5>起拍价: ${{ listing.startingBid }}</h5>
<h5>当前价格: ${% if listing.price %}{{ listing.price.bid }}{% else %}{{ listing.startingBid }}{% endif %}</h5>

<!-- 出价输入框最小值设为起拍价 -->
<input type="number" min="{{ listing.startingBid }}" name="newBid" placeholder="输入出价">

验证要点

  1. 创建新拍卖时,自动生成初始Bid,价格等于起拍价。
  2. 出价低于起拍价时,返回对应错误提示。
  3. 出价等于当前最高价格时,返回错误提示。
  4. 出价符合规则时,更新当前价格并显示成功提示。
  5. 拍卖关闭后,最高出价用户能看到中标提示。

内容的提问来源于stack exchange,提问作者Javier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:54:59