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 %}
问题分析
- 模型类型不匹配:Listing中的
price字段定义为IntegerField,但在views中却将其赋值为Bid对象,会导致类型错误,模板访问listing.price.bid时会抛出属性不存在的异常(整数没有bid属性)。 - 出价逻辑缺失起拍价对比:当前代码仅对比新出价与当前
price.bid,未检查是否大于等于startingBid,不符合需求。 - Bid记录未关联Listing:创建新Bid时未绑定对应拍卖,导致Bid数据孤立,无法追踪归属。
- 模板变量不一致: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="输入出价">
验证要点
- 创建新拍卖时,自动生成初始Bid,价格等于起拍价。
- 出价低于起拍价时,返回对应错误提示。
- 出价等于当前最高价格时,返回错误提示。
- 出价符合规则时,更新当前价格并显示成功提示。
- 拍卖关闭后,最高出价用户能看到中标提示。
内容的提问来源于stack exchange,提问作者Javier
相关产品推荐
相关产品推荐

